Android Input系统事件分发分析

news2024/11/29 0:44:03

“本文基于Android13源码,分析Input系统中,事件分发的实现原理“

整个事件分发到事件的确认过程很长,如果读者只是想大概了解一下,可以直接看末尾总结部分的流程图。

1. 前言

在文章之前,有必要提一下InputReader。其在启动的时候,会创建一个InputReader线程,用于从/dev/input节点获取事件,转换成EventEntry事件加入到InputDispatcher的mInboundQueue。详情见 Input系统—InputReader线程

Inputdispatcher 则负责消费mInboundQueue中的事件,并将事件转化后发送给app端,他们的关系如下:
Alt

1.1 InputDispatcher

Inputdispatcher中,在线程里面调用到dispatchOnce方法,该方法中主要做:

  • 通过dispatchOnceInnerLocked(),取出mInboundQueue 里面的 EventEntry事件
  • 通过enqueueDispatchEntryLocked(),生成事件DispatchEntry并加入connection的outbound队列。
  • 通过startDispatchCycleLocked(),从outboundQueue中取出事件DispatchEntry, 重新放入connection的waitQueue队列。
  • 通过runCommandsLockedInterruptable(),遍历mCommandQueue队列,依次处理所有命令。
  • 通过processAnrsLocked(),判断是否需要触发ANR。
  • 在startDispatchCycleLocked()里面,通过inputPublisher.publishKeyEvent() 方法将按键事件分发给java层。publishKeyEvent的实现是在InputTransport.cpp 中

通过上面的概括,可以知道按键事件主要存储在3个queue中:

  1. InputDispatcher的mInboundQueue:存储的是从InputReader 送来的输入事件。
  2. Connection的outboundQueue:该队列是存储即将要发送给应用的输入事件。
  3. Connection的waitQueue:队列存储的是已经发给应用的事件,但是应用还未处理完成的。

1.2 InputEventInjectionResult

在处理按键分发过程中,经常会遇到该枚举,对应含义如下

enum InputEventInjectionResult {
    /* (仅限内部使用)指定注入挂起且其结果未知 */
    PENDING = -1,

    /* 事件注射成功. */
    SUCCEEDED = 0,

    /* 注入失败,因为注入的事件未针对相应的窗口 */
    TARGET_MISMATCH = 1,

    /* 注入失败,因为没有可用的输入目标。*/
    FAILED = 2,

    /* Injection failed due to a timeout. */
    TIMED_OUT = 3,
}

2. 事件分发

InputDispatcher 在start() 方法中会启动一个线程,在被唤醒后会调用到dispatchOnce 方法,在该方法中,通过调用dispatchOnceInnerLocked来分发事件

> frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    {
        ...
        // 如果没有挂起的命令,则运行调度循环。调度循环可能会将命令排入队列,以便稍后运行。
        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(&nextWakeupTime);
        }
        ....
    }
    // 等待回调、超时或唤醒。
    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}

2.1 dispatchOnceInnerLocked

dispatchOnceInnerLocked主要是:

  • 从mInboundQueue 中取出mPendingEvent

  • 通过mPendingEvent的type决定事件类型和分发方式。

  • 最后如果处理了事件,就处理相关的回收。

主要代码如下:

> frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
  
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    nsecs_t currentTime = now();
    ...
    // 优化应用切换的延迟。本质上,当按下应用程序切换键(HOME)时,我们会开始一个短暂的超时。
    // 当它过期时,我们会抢占调度并删除所有其他挂起的事件。
    bool isAppSwitchDue = mAppSwitchDueTime <= currentTime; // mAppSwitchDueTime是应用切换到期时间
    if (mAppSwitchDueTime < *nextWakeupTime) {
        *nextWakeupTime = mAppSwitchDueTime; // 更新下一次唤醒的时间
    }
    // 当前没有PendingEvent(即EventEntry),则取一个
    if (!mPendingEvent) {
        // 1、mInboundQueue 为空
        if (mInboundQueue.empty()) {
            // 如果合适就合成一个重复按键。
            if (mKeyRepeatState.lastKeyEntry) {
                if (currentTime >= mKeyRepeatState.nextRepeatTime) {
                    // 用于创建一个新的repeat按键
                    mPendingEvent = synthesizeKeyRepeatLocked(currentTime);
                } else {
                    if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
                        *nextWakeupTime = mKeyRepeatState.nextRepeatTime;
                    }
                }
            }
            // 如果没有PendingEvent,就直接返回
            if (!mPendingEvent) {
                return;
            }
        } else {
        // 2、mInboundQueue不为空,就从队列前面取一个PendingEvent
            mPendingEvent = mInboundQueue.front();
            mInboundQueue.pop_front();
            traceInboundQueueLengthLocked();
        }
        if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            // 根据当前的event 类型,post 一个 command 到 mCommandQueue
            // 最后是调用到Java层的PowerManagerService#userActivityFromNative()
            pokeUserActivityLocked(*mPendingEvent);
        }
    }
    // 现在我们有一个事件要发送。所有事件最终都会以这种方式取消排队和处理,即使我们打算删除它们。
    ...
    bool done = false;
    DropReason dropReason = DropReason::NOT_DROPPED;
    ...
    switch (mPendingEvent->type) {
        ...
        case EventEntry::Type::KEY: {
            ...
            // 最后会调用到dispatchEventLocked
            done = dispatchKeyLocked(currentTime, keyEntry, &dropReason, nextWakeupTime);
            break;
        }
        case EventEntry::Type::MOTION: {
            ...
            // 最后会调用到dispatchEventLocked
            done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
            break;
        }
    }
    if (done) { 
        if (dropReason != DropReason::NOT_DROPPED) {
            // 事件没有被丢弃。找到对应的原因并通知
            dropInboundEventLocked(*mPendingEvent, dropReason);
        }
        mLastDropReason = dropReason;
        // 将mPendingEvent 置为 Null,方便下次重新获取
        releasePendingEventLocked();
        *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
    }
}

2.1.1 dispatchKeyLocked

> frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
    
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
                                        DropReason* dropReason, nsecs_t* nextWakeupTime) {
  //预处理:主要处理按键重复问题
  if (!entry->dispatchInProgress) {
       ...
          if (mKeyRepeatState.lastKeyEntry &&
                mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode &&
                // 我们已经看到连续两个相同的按键,这表明设备驱动程序正在自动生成键重复。
                // 我们在这里记下重复,但我们禁用了自己的下一个键重复计时器,因为很明显我们不需要自己合成键重复。
                mKeyRepeatState.lastKeyEntry->deviceId == entry->deviceId) {
                // 确保我们不会从其他设备获取密钥。如果按下了相同的设备 ID,则新的设备 ID 将替换当前设备 ID 以按住重复键并重置重复计数。
                // 将来,当设备ID上出现KEY_UP时,请将其删除,并且不要停止当前设备上的密钥重复。
                entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
                resetKeyRepeatLocked();
                mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // 不要自己生成重复
            } else {
                //不是重复。保存按键down状态,以防我们稍后遇到重复。
                resetKeyRepeatLocked();
                mKeyRepeatState.nextRepeatTime = entry->eventTime + mConfig.keyRepeatTimeout;
            }
            mKeyRepeatState.lastKeyEntry = entry;
       ...
    }
    // 处理policy 上次要求我们重试的情况
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
         // 当前时间 < 唤醒时间,则进入等待状态
        if (currentTime < entry->interceptKeyWakeupTime) {
            if (entry->interceptKeyWakeupTime < *nextWakeupTime) {
                *nextWakeupTime = entry->interceptKeyWakeupTime;
            }
            return false; // 等到下次醒来
        }
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
        entry->interceptKeyWakeupTime = 0;
    }
    // 给policy提供拦截key的机会。
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
        if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            sp<IBinder> focusedWindowToken =
                    mFocusResolver.getFocusedWindowToken(getTargetDisplayId(*entry));

            auto command = [this, focusedWindowToken, entry]() REQUIRES(mLock) {
                doInterceptKeyBeforeDispatchingCommand(focusedWindowToken, *entry);
            };
            postCommandLocked(std::move(command));
            return false; // wait for the command to run
        } else {
            entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
        }
    } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
        if (*dropReason == DropReason::NOT_DROPPED) {
            *dropReason = DropReason::POLICY;
        }
    }
    // 如果删除事件,则清理。
    if (*dropReason != DropReason::NOT_DROPPED) {
        setInjectionResult(*entry,
                           *dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED
                                                             : InputEventInjectionResult::FAILED);
        mReporter->reportDroppedKey(entry->id);
        return true;
    }
    // 寻找输入目标
    std::vector<InputTarget> inputTargets;
    InputEventInjectionResult injectionResult =
            findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
    if (injectionResult == InputEventInjectionResult::PENDING) {
        return false;
    }
    setInjectionResult(*entry, injectionResult);
    if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
        return true;
    }
    // 从事件或焦点显示添加监视器通道。
    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
    // 分发按键
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

2.1.2 dispatchMotionLocked

> frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
    
bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,DropReason* dropReason, nsecs_t* nextWakeupTime) {
    ...
    // 是否是指针事件
    const bool isPointerEvent = isFromSource(entry->source, AINPUT_SOURCE_CLASS_POINTER);
    // Identify targets.
    std::vector<InputTarget> inputTargets;
    bool conflictingPointerActions = false;
    InputEventInjectionResult injectionResult;
    if (isPointerEvent) {  // 指针事件。(例如触摸屏)
        // 查找已锁定的触摸窗口目标,主要根据读取到的触控事件所属的屏幕displayid、x、y坐标位置等属性来确认目标窗口
        // 这里不是我们的重点
        injectionResult =
                findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
    } else {  // 非触摸事件。(例如轨迹球)
        injectionResult =
                findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
    }
    if (injectionResult == InputEventInjectionResult::PENDING) {
        // 如果是pending就直接返回
        return false;
    }
    ....
    // 从事件或焦点显示添加监视器通道。
    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
    // 分发事件
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

2.2 dispatchEventLocked

dispatchEventLocked 主要是遍历inputTargets,通过prepareDispatchCycleLocked分发事件。

> frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
  
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
                                          std::shared_ptr<EventEntry> eventEntry,
                                          const std::vector<InputTarget>& inputTargets) {
    ...
    for (const InputTarget& inputTarget : inputTargets) {
        sp<Connection> connection =
                getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
        if (connection != nullptr) {
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
        }
    }
}

2.3 prepareDispatchCycleLocked

prepareDispatchCycleLocked内部又会调用enqueueDispatchEntriesLocked方法。


void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
                                                 const sp<Connection>& connection,
                                                 std::shared_ptr<EventEntry> eventEntry,
                                                 const InputTarget& inputTarget) {
  
    // 不分裂。按原样将事件的调度条目排队。
    enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}

2.4 enqueueDispatchEntriesLocked

主要做两个事情:

  • 1)将请求模式的调度条目排队。
  • 2)启动调度周期。
> frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
  
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
                                                   const sp<Connection>& connection,
                                                   std::shared_ptr<EventEntry> eventEntry,
                                                   const InputTarget& inputTarget) {
    bool wasEmpty = connection->outboundQueue.empty();
    // 将请求模式的调度条目排队。
    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
                               InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
    ...
    // 如果出站队列以前为空,请开始调度周期。
    if (wasEmpty && !connection->outboundQueue.empty()) {
        startDispatchCycleLocked(currentTime, connection);
    }
}

2.4.1 enqueueDispatchEntryLocked

enqueueDispatchEntryLocked 会创建一个新的DispatchEntry,然后将DispatchEntry 加入到connection的outboundQueue中

> frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
  
void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection,
                                                 std::shared_ptr<EventEntry> eventEntry,
                                                 const InputTarget& inputTarget,
                                                 int32_t dispatchMode) {
    // 这是一个新事件。将新的调度条目排队到此连接的出站队列中。
    std::unique_ptr<DispatchEntry> dispatchEntry =
            createDispatchEntry(inputTarget, eventEntry, inputTargetFlags);
    ...
    // 将生成的 dispatchEntry 加入到 connection的outboundQueue 中
    connection->outboundQueue.push_back(dispatchEntry.release());
    traceOutboundQueueLength(*connection);
}

2.4.2 startDispatchCycleLocked

该方法主要通过connection 发布最终的事件,至此,InputDispatcher完成事件的发布,并且将发布的事件保存在connection的waitQueue中。

> frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
  
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
                                               const sp<Connection>& connection) {
    while (connection->status == Connection::Status::NORMAL && !connection->outboundQueue.empty()) {
         // 从outboundQueue 队列中取出 DispatchEntry
        DispatchEntry* dispatchEntry = connection->outboundQueue.front();
        // 发布事件
        status_t status;
        const EventEntry& eventEntry = *(dispatchEntry->eventEntry);
        switch (eventEntry.type) {
            case EventEntry::Type::KEY: {
                ...
                // 发布按键事件
                status = connection->inputPublisher
                                 .publishKeyEvent(dispatchEntry->seq ...);
                break;
            }
            case EventEntry::Type::MOTION: {
                ...
                // 发布运动事件。
                status = connection->inputPublisher
                                 .publishMotionEvent(dispatchEntry->seq ...);
                break;
            }
            ....
        }
        // 如果status 已赋值
        if (status) {
            if (status == WOULD_BLOCK) {
                if (connection->waitQueue.empty()) {
                    // pip 管道满了导致无法发布事件。
                    // 该问题是出乎意料的,因为等待队列是空的,所以管道应该是空的
                    
                    // 将outboundQueue,waitQueue 队列清空,并释放队列中的DispatchEntry 对象
                    abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
                } else {
                    // 管道已满,并且waitQueue 不为空,我们正在等待应用程序完成处理一些事件,然后再向其发送更多事件。
                }
            } else {
                // 将outboundQueue,waitQueue 队列清空,并释放队列中的DispatchEntry 对象
                abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
            }
            return;
        }
        // 将事件从outboundQueue中移除
        connection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(),
                                                    connection->outboundQueue.end(),
                                                    dispatchEntry));
        // 在waitQueue 尾部重新插入
        connection->waitQueue.push_back(dispatchEntry);
    }
}

2.5 publishMotionEvent

查看connection的头文件,可以知道connection->inputPublisher 是InputPublisher类

> frameworks/native/services/inputflinger/dispatcher/Connection.h

class Connection : public RefBase {
public:
    InputPublisher inputPublisher;
}

InputPublisher class的定义如下,负责将输入事件发布到输入通道。

> frameworks/native/include/input/InputTransport.h
  
// 负责将输入事件发布到输入通道。
class InputPublisher {
 ....
private:
    std::shared_ptr<InputChannel> mChannel;
}

class InputChannel : public Parcelable {
}

publishMotionEvent 对应的实现如下

> frameworks/native/libs/input/InputTransport.cpp
  
status_t InputPublisher::publishMotionEvent(
             uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, int32_t displayId ..) {
    ....
    InputMessage msg;
    // 设置event的参数
    msg.header.type = InputMessage::Type::MOTION;
    msg.body.motion.action = action;
    ....
    // 调用InputChannel的sendMessage方法
    return mChannel->sendMessage(&msg);
}

mChannel即 InputChannel

> frameworks/native/include/input/InputTransport.h

private:
    std::shared_ptr<InputChannel> mChannel;

2.6 runCommandsLockedInterruptable

dispatchOnceInnerLocked已经分析完,我们再次回到dispatchOnce,分析runCommandsLockedInterruptable方法

> frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
  
void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    {
        ...
        // 运行所有挂起的命令(如果有)。如果运行了任何命令,则强制下一次轮询立即唤醒。
        if (runCommandsLockedInterruptable()) {
            nextWakeupTime = LONG_LONG_MIN;
        }
        ...
    }
}

该方法很简单,就是遍历mCommandQueue 执行对应的command。

> frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
  
bool InputDispatcher::runCommandsLockedInterruptable() {
    do {
        auto command = std::move(mCommandQueue.front());
        mCommandQueue.pop_front();
        // Commands are run with the lock held, but may release and re-acquire the lock from within.
        // 命令在锁保持的情况下运行,但可能会从内部释放并重新获取锁。
        command();
    } while (!mCommandQueue.empty());
    return true;
}

通过postCommandLocked 方法将command添加到队列中

void InputDispatcher::postCommandLocked(Command&& command) {
    mCommandQueue.push_back(command);
}

2.7 调用栈

native层的事件分发调用栈如下

libs/input/InputTransport.cpp : InputPublisher::publishMotionEvent()
services/inputflinger/dispatcher/InputDispatcher.cpp : InputDispatcher::startDispatchCycleLocked()
services/inputflinger/dispatcher/InputDispatcher.cpp : InputDispatcher::enqueueDispatchEntriesLocked()
services/inputflinger/dispatcher/InputDispatcher.cpp : InputDispatcher::prepareDispatchCycleLocked()
services/inputflinger/dispatcher/InputDispatcher.cpp : InputDispatcher::dispatchKeyLocked()
services/inputflinger/dispatcher/InputDispatcher.cpp : InputDispatcher::dispatchOnceInnerLocked()
services/inputflinger/dispatcher/InputDispatcher.cpp : InputDispatcher::dispatchOnce()
services/inputflinger/dispatcher/InputDispatcher.cpp : InputDispatcher::start()

3. InputChannel

InputChannel负责将消息发送给到app端,那它是如何做到的呢?

3.1 sendMessage

InputChannel的sendMessage 方法定义如下,

> frameworks/native/libs/input/InputTransport.cpp
  
status_t InputChannel::sendMessage(const InputMessage* msg) {
    ....
    ssize_t nWrite;
    do {
        nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);
    ....
    return OK;
}

其中fd很关键,当fd 写入消息的时候,会唤醒处于epoll_wait 状态的线程(原理跟handler一样)

那他是如何跟app端的fd绑定的呢?

3.2 客户端创建FD

在ViewRootImpl的setView方法中,

> frameworks/base/core/java/android/view/ViewRootImpl.java
 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
       if ((mWindowAttributes.inputFeatures
               & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
          mInputChannel = new InputChannel();
       }
       try {
        mOrigWindowType = mWindowAttributes.type;
        mAttachInfo.mRecomputeGlobalAttributes = true;
        collectViewAttributes();
        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); // 将mInputChannel 注册到WMS中
       } catch (RemoteException e) {
         ...
       }
       if (mInputChannel != null) {
          if (mInputQueueCallback != null) {
              mInputQueue = new InputQueue();
              mInputQueueCallback.onInputQueueCreated(mInputQueue);
          }
          // 创建app端监听,即WindowInputEventReceiver 作为事件的接收端
          mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                                                             Looper.myLooper());
        }
 }

mWindowSession.addToDisplay 对应AIDL实现为

> frameworks/base/services/core/java/com/android/server/wm/Session.java
  
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
    @Override
    public int addToDisplay(IWindow window .... DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
       // mService 即 WindowManagerService
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
                outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
    }

}

WindowManagerService实现如下

>  frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

public int addWindow(Session session, IWindow client, int seq,
            LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {

  final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
   final boolean openInputChannels = (outInputChannel != null
                    && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
   if  (openInputChannels) {
       // 通过WindowState 打开对应的inputChannel
       win.openInputChannel(outInputChannel);
   }
}

WindowState实现如下

> frameworks/base/services/core/java/com/android/server/wm/WindowState.java

// 输入调度程序使用的输入通道和输入窗口句柄。
final InputWindowHandle mInputWindowHandle;
void openInputChannel(InputChannel outInputChannel) {
        String name = getName();
        InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
        mInputChannel = inputChannels[0]; 
        mClientChannel = inputChannels[1];
        mInputWindowHandle.inputChannel = inputChannels[0];
        if (outInputChannel != null) {
            mClientChannel.transferTo(outInputChannel);
            mClientChannel.dispose();
            mClientChannel = null;
        } else {
            mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
        }
        //  将inputChannel注册到InputManagerService中
        mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
}

InputManagerService 如下,最后注册到native中

> frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

// 注册输入通道,以便将其用作输入事件目标。
public void registerInputChannel(InputChannel inputChannel,
            InputWindowHandle inputWindowHandle) {
        nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
}

3.3 native层注册

nativeRegisterInputChannel 实现如下

> frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
  

static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
        jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    if (inputChannel == NULL) {
        throwInputChannelNotInitialized(env);
        return;
    }

    sp<InputWindowHandle> inputWindowHandle =
            android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);

    status_t status = im->registerInputChannel(
            env, inputChannel, inputWindowHandle, monitor);
    if (status) {
        std::string message;
        message += StringPrintf("Failed to register input channel.  status=%d", status);
        jniThrowRuntimeException(env, message.c_str());
        return;
    }

    if (! monitor) {
        android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
                handleInputChannelDisposed, im);
    }
}

3.4 接收端

3.4.1 初始化

WindowInputEventReceiver对应构造函数如下

> frameworks/base/core/java/android/view/ViewRootImpl.java

final class WindowInputEventReceiver extends InputEventReceiver {
        public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
            super(inputChannel, looper);
        }
}

其父类实现如下

>  frameworks/base/core/java/android/view/InputEventReceiver.java

public InputEventReceiver(InputChannel inputChannel, Looper looper) {
        mInputChannel = inputChannel;
        mMessageQueue = looper.getQueue(); // UI线程的looper
        mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                inputChannel, mMessageQueue); // 通过nativeInit 与inputChannel绑定
        mCloseGuard.open("dispose");
}

3.4.2 nativeInit

> frameworks/base/core/jni/android_view_InputEventReceiver.cpp

static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    // 获取主线程的消息队列
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    // 创建一个NativeInputEventReceiver对象
    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    status_t status = receiver->initialize();
    .....
    receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
    return reinterpret_cast<jlong>(receiver.get());
}

3.4.3 NativeInputEventReceiver

initialize 实现如下

> frameworks/base/core/jni/android_view_InputEventReceiver.cpp
class NativeInputEventReceiver : public LooperCallback {
   .....
}
status_t NativeInputEventReceiver::initialize() {
    setFdEvents(ALOOPER_EVENT_INPUT);
    return OK;
}

setFdEvents 如下

> frameworks/base/core/jni/android_view_InputEventReceiver.cpp
  
void NativeInputEventReceiver::setFdEvents(int events) {
    if (mFdEvents != events) {
        mFdEvents = events;
        int fd = mInputConsumer.getChannel()->getFd();
        if (events) {
            //将FD添加到消息池中
            mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
        } else {
            mMessageQueue->getLooper()->removeFd(fd);
        }
    }
}

当事件分发的时候,looper会调用到NativeInputEventReceiver的handleEvent 方法

> system/core/libutils/Looper.cpp
int Looper::pollInner(int timeoutMillis) {
      ....
      int callbackResult = response.request.callback->handleEvent(fd, events, data);
      if (callbackResult == 0) {
         AutoMutex _l(mLock);
         removeSequenceNumberLocked(response.seq);
      }
      ....
}

NativeInputEventReceiver的handleEvent 对应实现如下:

> frameworks/base/core/jni/android_view_InputEventReceiver.cpp

int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    ....
    if (events & ALOOPER_EVENT_INPUT) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        // 调用consumeEvents 来消费事件
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
        return status == OK || status == NO_MEMORY ? 1 : 0;
    }
}

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
           .....
           if (inputEventObj) {
                if (kDebugDispatchCycle) {
                    ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName().c_str());
                }
                // 调用java层的InputEventReceiver#dispatchInputEvent方法
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj,
                        displayId);
                if (env->ExceptionCheck()) {
                    ALOGE("Exception dispatching input event.");
                    skipCallbacks = true;
                }
                env->DeleteLocalRef(inputEventObj);
           }
}

4. Activity事件接收

4.1 WindowInputEventReceiver

从上面得知,事件会分发到InputEventReceiver#dispatchInputEvent方法中

> frameworks/base/core/java/android/view/InputEventReceiver.java
  
private void dispatchInputEvent(int seq, InputEvent event, int displayId) {
     mSeqMap.put(event.getSequenceNumber(), seq);
     onInputEvent(event, displayId);
}

WindowInputEventReceiver 重写了onInputEvent 方法

> frameworks/base/core/java/android/view/ViewRootImpl.java
  
final class WindowInputEventReceiver extends InputEventReceiver {
   @Override
    public void onInputEvent(InputEvent event, int displayId) {
        enqueueInputEvent(event, this, 0, true);
    }
    ...
}

4.2 ViewRooImpl

4.2.1 enqueueInputEvent

> frameworks/base/core/java/android/view/ViewRootImpl.java

void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {
        adjustInputEventForCompatibility(event);
        QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
        // 始终按顺序对输入事件进行排队,而不考虑其时间戳。我们这样做是因为应用程序或 IME 可能会注入密钥事件以响应触摸事件,
        // 我们希望确保按接收顺序处理注入的密钥,并且我们不能相信注入事件的时间戳是单调的。
        QueuedInputEvent last = mPendingInputEventTail;
        if (last == null) {
            mPendingInputEventHead = q;
            mPendingInputEventTail = q;
        } else {
            last.mNext = q;
            mPendingInputEventTail = q;
        }
        mPendingInputEventCount += 1;
        if (processImmediately) {
            doProcessInputEvents(); // 分发输入事件
        } else {
            scheduleProcessInputEvents();
        }
}

4.2.2 doProcessInputEvents

> frameworks/base/core/java/android/view/ViewRootImpl.java

    void doProcessInputEvents() {
        // 传递队列中所有pending 输入事件。
        while (mPendingInputEventHead != null) {
            QueuedInputEvent q = mPendingInputEventHead; // QueuedInputEvent 封装了KeyEvent的信息
            mPendingInputEventHead = q.mNext;
            if (mPendingInputEventHead == null) {
                mPendingInputEventTail = null;
            }
            q.mNext = null;
            mPendingInputEventCount -= 1;
            long eventTime = q.mEvent.getEventTimeNano();
            long oldestEventTime = eventTime;
            if (q.mEvent instanceof MotionEvent) {
                MotionEvent me = (MotionEvent)q.mEvent;
                if (me.getHistorySize() > 0) {
                    oldestEventTime = me.getHistoricalEventTimeNano(0);
                }
            }
            // 更新输入事件时间
            mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);
            deliverInputEvent(q);// 给应用提供输入事件
        }
    }

4.2.3 deliverInputEvent

> frameworks/base/core/java/android/view/ViewRootImpl.java
  private void deliverInputEvent(QueuedInputEvent q) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
        }
        InputStage stage;
        if (q.shouldSendToSynthesizer()) {
            stage = mSyntheticInputStage;
        } else {
            // shouldSkipIme 用于判断是否跳过输入法,默认是跳过
            stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
        }
        if (q.mEvent instanceof KeyEvent) {
            mUnhandledKeyManager.preDispatch((KeyEvent) q.mEvent);
        }
        if (stage != null) {
            handleWindowFocusChanged();
            stage.deliver(q); // 通过InputStage分发事件
        } else {
            finishInputEvent(q);
        }
  }

我们先考虑跳过输入法的场景,故这里的stage是mFirstPostImeInputStage,mFirstPostImeInputStage在setView中赋值,对应是EarlyPostImeInputStage

> frameworks/base/core/java/android/view/ViewRootImpl.java
  
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    ....
    InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
    InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                        "aq:native-post-ime:" + counterSuffix);
    InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
    InputStage imeStage = new ImeInputStage(earlyPostImeStage,"aq:ime:" + counterSuffix);
    InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
    InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,"aq:native-pre-ime:" + counterSuffix);
    mFirstInputStage = nativePreImeStage;
    mFirstPostImeInputStage = earlyPostImeStage;
    ....
}

4.5 InputStage

InputStage 是一个链表结构,其内部存储下一个InputStage,采用拦截器模式来分发事件。

4.5.1 deliver

> frameworks/base/core/java/android/view/ViewRootImpl.java

abstract class InputStage {
    private final InputStage mNext;
    public final void deliver(QueuedInputEvent q) {
            if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
                forward(q);
            } else if (shouldDropInputEvent(q)) {
                finish(q, false);
            } else {
                apply(q, onProcess(q)); // 调用到子类的onProcess来决定是否需要拦截该事件
            }
        }
}

4.6 EarlyPostImeInputStage

EarlyPostImeInputStage 的实现如下

> frameworks/base/core/java/android/view/ViewRootImpl.java
   
    final class EarlyPostImeInputStage extends InputStage {
        public EarlyPostImeInputStage(InputStage next) {
            super(next);
        }

        @Override
        protected int onProcess(QueuedInputEvent q) {
            if (q.mEvent instanceof KeyEvent) {
                return processKeyEvent(q);
            } else {
                final int source = q.mEvent.getSource();
                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                    return processPointerEvent(q);
                }
            }
            return FORWARD; // 由于是触摸事件,这里会转发给下一个拦截器
        }
        .....
    }

EarlyPostImeInputStage没有对触摸事件做拦截,故会分发给NativePostImeInputStage

4.7 NativePostImeInputStage

定义如下

> frameworks/base/core/java/android/view/ViewRootImpl.java
  
    final class NativePostImeInputStage extends AsyncInputStage
            implements InputQueue.FinishedInputEventCallback {
        public NativePostImeInputStage(InputStage next, String traceCounter) {
            super(next, traceCounter);
        }

        @Override
        protected int onProcess(QueuedInputEvent q) {
            if (mInputQueue != null) {
                mInputQueue.sendInputEvent(q.mEvent, q, false, this);
                return DEFER;
            }
            return FORWARD;
        }
    }

mInputQueue 是ViewRootImpl的变量,通过debug发现mInputQueue为null,故会继续转发给ViewPostImeInputStage。

在这里插入图片描述

4.8 ViewPostImeInputStage

实现如下

> frameworks/base/core/java/android/view/ViewRootImpl.java
  
    final class ViewPostImeInputStage extends InputStage {
        public ViewPostImeInputStage(InputStage next) {
            super(next);
        }

        @Override
        protected int onProcess(QueuedInputEvent q) {
            if (q.mEvent instanceof KeyEvent) {
                return processKeyEvent(q);
            } else {
                final int source = q.mEvent.getSource(); //source值是20482
                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                    return processPointerEvent(q);
                } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                    return processTrackballEvent(q);
                } else {
                    return processGenericMotionEvent(q);
                }
            }
        }
    }

通过计算得知source & InputDevice.SOURCE_CLASS_POINTER != 0,故会调用到processPointerEvent 方法

在这里插入图片描述

4.8.1 processPointerEvent

实现如下:

> frameworks/base/core/java/android/view/ViewRootImpl.java
  
    final class ViewPostImeInputStage extends InputStage {
        ....
        private int processPointerEvent(QueuedInputEvent q) {
            final MotionEvent event = (MotionEvent)q.mEvent;
            mAttachInfo.mUnbufferedDispatchRequested = false;
            mAttachInfo.mHandlingPointerEvent = true;
            boolean handled = mView.dispatchPointerEvent(event); // 即调用到DecorView的dispatchPointerEvent方法
            ....
            return handled ? FINISH_HANDLED : FORWARD; // 如果已经消费事件,就拦截事件,返回finish。
        }
    }

其中mView即为ViewRootImpl中的DecorView

在这里插入图片描述

4.9 DecorView

4.9.1 dispatchPointerEvent

dispatchPointerEvent实现如下

> frameworks/base/core/java/android/view/View.java

    public final boolean dispatchPointerEvent(MotionEvent event) {
        if (event.isTouchEvent()) {
            return dispatchTouchEvent(event);
        } else {
            return dispatchGenericMotionEvent(event);
        }
    }

这里是触摸事件,故会分发给dispatchTouchEvent

4.9.2 dispatchTouchEvent

DecorView 重写了dispatchTouchEvent 方法,内部通过window.callback来决定事件分发。

> frameworks/base/core/java/com/android/internal/policy/DecorView.java

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        final Window.Callback cb = mWindow.getCallback(); // 通过phineWindow获取callBack
        return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
                ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
    }

Activity 在attach的时候,会通过phonewindow注册监听

> frameworks/base/core/java/android/app/Activity.java
  
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        ....
    }

故最终分发到Activity的dispatchTouchEvent 中

4.10 调用栈

frameworks/base/core/java/android/app/Activity.java : Activity.dispatchTouchEvent()
frameworks/base/core/java/com/android/internal/policy/DecorView.java : DecorView.dispatchTouchEvent()
frameworks/base/core/java/android/view/View.java : View.dispatchPointerEvent()
frameworks/base/core/java/android/view/ViewRootImpl.java : ViewPostImeInputStage.processPointerEvent()
frameworks/base/core/java/android/view/ViewRootImpl.java : ViewPostImeInputStage.onProcess()
frameworks/base/core/java/android/view/ViewRootImpl.java : NativePostImeInputStage.onProcess()
frameworks/base/core/java/android/view/ViewRootImpl.java : EarlyPostImeInputStage.onProcess()
frameworks/base/core/java/android/view/ViewRootImpl.java : ViewRootImpl.deliverInputEvent()
frameworks/base/core/java/android/view/ViewRootImpl.java : ViewRootImpl.enqueueInputEvent()
frameworks/base/core/java/android/view/InputEventReceiver.java : InputEventReceiver.onInputEvent()
frameworks/base/core/java/android/view/InputEventReceiver.java : InputEventReceiver.dispatchInputEvent()

5. View 事件分发

5.1 Activity

dispatchTouchEvent 实现如下

> frameworks/base/core/java/android/app/Activity.java

    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev); // 如果window没有消费,就默认传给activity的onTouchEvent方法。
    }
    // 该方法默认不消费
    public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }
        return false;
    }

5.2 PhoneWindow

superDispatchTouchEvent的实现如下,只是调用DecorView的superDispatchTouchEvent

> frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java

    @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }

superDispatchTouchEvent 实现也很简单,直接调用ViewGroup 的 dispatchTouchEvent

> frameworks/base/core/java/com/android/internal/policy/DecorView.java

    public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }

5.3 ViewGroup

5.3.1 dispatchTouchEvent

view层的事件,最终是由ViewGroup去分发的,在分发的时候,会优先分发给子view

> frameworks/base/core/java/android/view/ViewGroup.java

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
             // 1、检查是否拦截
            final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) { // 有触摸目标或者action为down
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    intercepted = onInterceptTouchEvent(ev); // 判断是否拦截,默认false
                    ev.setAction(action); 
                }
            } else {
                // 没有触摸目标,此操作不是初始关闭,因此此视图组继续拦截触摸。
                intercepted = true;
            }
            // 2、检查是否取消
            final boolean canceled = resetCancelNextUpFlag(this)
                    || actionMasked == MotionEvent.ACTION_CANCEL;
            // 没有取消,也没有被拦截       
            if (!canceled && !intercepted) {
                // 找一个可以接收事件的孩子。从前到后扫描mChildren。
                final View[] children = mChildren;
                for (int i = childrenCount - 1; i >= 0; i--) {
                    if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                      // childView想在边界内接受触摸。
                    }
                }
            }
            // 分发给触摸目标。
            if (mFirstTouchTarget == null) {
                // 没有触摸目标,因此请将其视为普通视图
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            }
            ....
    }

5.3.2 onInterceptTouchEvent

默认不做拦截

> frameworks/base/core/java/android/view/ViewGroup.java
   
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
                && ev.getAction() == MotionEvent.ACTION_DOWN
                && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
                && isOnScrollbarThumb(ev.getX(), ev.getY())) {
            // 事件在滚动条上
            return true;
        }
        return false;
    }

5.3.3 dispatchTransformedTouchEvent

该方法负责将运动事件转换为特定子视图的坐标空间,如果子项为空,则假定 MotionEvent 将改为发送到此视图组。

> frameworks/base/core/java/android/view/ViewGroup.java
  
    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
       ....
        // 执行任何必要的转换和调度。
        if (child == null) {
            handled = super.dispatchTouchEvent(transformedEvent); // 调用View的dispatchTouchEvent
        } else {
            final float offsetX = mScrollX - child.mLeft;
            final float offsetY = mScrollY - child.mTop;
            transformedEvent.offsetLocation(offsetX, offsetY);
            if (! child.hasIdentityMatrix()) {
                transformedEvent.transform(child.getInverseMatrix());
            }
            handled = child.dispatchTouchEvent(transformedEvent); // 调用子View的dispatchTouchEvent
        }
        return handled;  
    }

最后都是调用到view的dispatchTouchEvent方法

5.4 View

5.4.1 dispatchTouchEvent

实现如下

> frameworks/base/core/java/android/view/View.java
  
   public boolean dispatchTouchEvent(MotionEvent event) {
      		.....
          if (onFilterTouchEventForSecurity(event)) {
            // 1、如果有设置OnTouchListener,就先交给mOnTouchListener 处理
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                   && (mViewFlags & ENABLED_MASK) == ENABLED
                   && li.mOnTouchListener.onTouch(this, event)) {
               result = true;
            }
            //2、如果没有处理,交给onTouchEvent方法
            if (!result && onTouchEvent(event)) {
                result = true;
            }
         }
	       ....
   }

5.4.2 onTouchEvent

在action down的时候,会延迟检查是否发生长按事件

> frameworks/base/core/java/android/view/View.java
   
  public boolean onTouchEvent(MotionEvent event) {
        // 1、如果设置了mTouchDelegate,就直接交给它处理
        if (mTouchDelegate != null) {
            if (mTouchDelegate.onTouchEvent(event)) {
                return true;
            }
        }
        // 如果可以点击或者支持在悬停或长按时显示工具提示。
        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
            switch (action) {
                case MotionEvent.ACTION_UP:
                   	 ....
                     // 仅当我们处于按下状态时才执行单击操作
                     if (!focusTaken) {
                         if (mPerformClick == null) {
                              mPerformClick = new PerformClick();
                         }
                        if (!post(mPerformClick)) {
                            performClickInternal();
                        }
                    }
                    break;
               case MotionEvent.ACTION_DOWN:
                    // 对于滚动容器内的视图,请将按下的反馈延迟一小段时间,以防这是滚动。
                    if (isInScrollingContainer) {
                        mPrivateFlags |= PFLAG_PREPRESSED;
                        if (mPendingCheckForTap == null) {
                            mPendingCheckForTap = new CheckForTap();
                        }
                        mPendingCheckForTap.x = event.getX();
                        mPendingCheckForTap.y = event.getY();
                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                    } else {
                        // 不在滚动容器内,因此请立即显示反馈
                        setPressed(true, x, y);
                        // 检查是否长按
                        checkForLongClick(0, x, y);
                    }
                    break;
        }
       return false;
  }

5.4.3 LongClick

checkForLongClick用于发送延时消息,触发长按

> frameworks/base/core/java/android/view/View.java
  
    private void checkForLongClick(int delayOffset, float x, float y) {
        if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE || (mViewFlags & TOOLTIP) == TOOLTIP) {
            if (mPendingCheckForLongPress == null) {
                mPendingCheckForLongPress = new CheckForLongPress();
            }
            mPendingCheckForLongPress.setAnchor(x, y);
            mPendingCheckForLongPress.rememberWindowAttachCount();
            mPendingCheckForLongPress.rememberPressedState();
            // 延迟消息触发长按事件
            postDelayed(mPendingCheckForLongPress,
                    ViewConfiguration.getLongPressTimeout() - delayOffset);
        }
    }

    private final class CheckForLongPress implements Runnable {
        @Override
        public void run() {
            if ((mOriginalPressedState == isPressed()) && (mParent != null)
                    && mOriginalWindowAttachCount == mWindowAttachCount) {
                // 触发长按
                if (performLongClick(mX, mY)) {
                    mHasPerformedLongPress = true;
                }
            }
        }
        ....
    }

5.4.4 onClick

在action up的时候,会post一个PerformClick

> frameworks/base/core/java/android/view/View.java

    private final class PerformClick implements Runnable {
        @Override
        public void run() {
            performClickInternal();
        }
    }

    private boolean performClickInternal() {
        // Must notify autofill manager before performing the click actions to avoid scenarios where
        // the app has a click listener that changes the state of views the autofill service might
        // be interested on.
        notifyAutofillManagerOnClick();

        return performClick();
    }

最后调用到performClick()

> frameworks/base/core/java/android/view/View.java
   public boolean performClick() {
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this); // 如果设置点击事件就调用onClick回调。
            result = true;
        } else {
            result = false;
        }
        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        notifyEnterOrExitForAutoFillIfNeeded(true);
        return result;
    }

5.5 调用栈

View 的点击事件调用栈如下

frameworks/base/core/java/android/view/View.java : View.performClick()
frameworks/base/core/java/android/view/View.java : View.performClickInternal()
frameworks/base/core/java/android/view/View.java : PerformClick.run()
frameworks/base/core/java/android/view/View.java : View.onTouchEvent()
frameworks/base/core/java/android/view/View.java : View.dispatchTouchEvent()
frameworks/base/core/java/android/view/ViewGroup.java : ViewGroup.dispatchTransformedTouchEvent()
frameworks/base/core/java/android/view/ViewGroup.java : ViewGroup.dispatchTouchEvent()
frameworks/base/core/java/com/android/internal/policy/DecorView.java : DecorView.superDispatchTouchEvent()
frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java : PhoneWindow.superDispatchTouchEvent()
frameworks/base/core/java/android/app/Activity.java : Activity.dispatchTouchEvent()

6. 事件确认

6.1 ViewPostImeInputStage

在ViewPostImeInputStage 中,如果我们的view处理了对应的事件,就会调用到finish方法,接着调用下一个处理器

> frameworks/base/core/java/android/view/ViewRootImpl.java
  
		protected void apply(QueuedInputEvent q, int result) {
            if (result == FORWARD) {
                forward(q);
            } else if (result == FINISH_HANDLED) {
                finish(q, true);
            } else if (result == FINISH_NOT_HANDLED) {
                finish(q, false);
            } else {
                throw new IllegalArgumentException("Invalid result: " + result);
            }
    }

    protected void finish(QueuedInputEvent q, boolean handled) {
        q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
        if (handled) {
           q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
        }
        forward(q);
    }
    // 调用下一个处理器
    protected void forward(QueuedInputEvent q) {
        onDeliverToNext(q);
    }

由初始化得知下一个处理器是SyntheticInputStage

> frameworks/base/core/java/android/view/ViewRootImpl.java
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
           mSyntheticInputStage = new SyntheticInputStage();
           InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
    }

6.2 SyntheticInputStage

6.2.1 onProcess

> frameworks/base/core/java/android/view/ViewRootImpl.java
  
final class SyntheticInputStage extends InputStage {
     public SyntheticInputStage() {
        super(null);
     }
  
     @Override
     protected int onProcess(QueuedInputEvent q) {
        q.mFlags |= QueuedInputEvent.FLAG_RESYNTHESIZED;
        if (q.mEvent instanceof MotionEvent) {
                final MotionEvent event = (MotionEvent)q.mEvent;
                final int source = event.getSource();
                if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { // 是轨迹球
                    mTrackball.process(event);
                    return FINISH_HANDLED;
                } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { // 是操纵杆
                    mJoystick.process(event);
                    return FINISH_HANDLED;
                } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
                        == InputDevice.SOURCE_TOUCH_NAVIGATION) { // 是摸导航
                    mTouchNavigation.process(event);
                    return FINISH_HANDLED;
                }
       } else if ((q.mFlags & QueuedInputEvent.FLAG_UNHANDLED) != 0) {
         mKeyboard.process((KeyEvent)q.mEvent);
         return FINISH_HANDLED;
       }
      return FORWARD;
   }
}

onProcess 返回值是FORWARD,会继续调用onDeliverToNext

在这里插入图片描述

forward 方法中直接调用onDeliverToNext

> frameworks/base/core/java/android/view/ViewRootImpl.java

abstract class InputStage {
      protected void forward(QueuedInputEvent q) {
            onDeliverToNext(q);
        }
}

6.2.2 onDeliverToNext

onProcess 返回值是FORWARD,在调用onDeliverToNext 的时候,由于next为null就会调用finish方法

> frameworks/base/core/java/android/view/ViewRootImpl.java
abstract class InputStage {
    protected void onDeliverToNext(QueuedInputEvent q) {
       if (mNext != null) {
          mNext.deliver(q);
       } else {
          finishInputEvent(q);
       }
    }
}

6.2.3 finishInputEvent

这里的mReceiver 是WindowInputEventReceiver

> frameworks/base/core/java/android/view/ViewRootImpl.java

private void finishInputEvent(QueuedInputEvent q) {
    if (q.mReceiver != null) {
        boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
        q.mReceiver.finishInputEvent(q.mEvent, handled);
    } else {
        q.mEvent.recycleIfNeededAfterDispatch();
    }
    // 回收队列中的输入事件
    recycleQueuedInputEvent(q);
}

6.3 WindowInputEventReceiver

finishInputEvent 的实现如下

> frameworks/base/core/java/android/view/InputEventReceiver.java
public final void finishInputEvent(InputEvent event, boolean handled) {
       ....
       int index = mSeqMap.indexOfKey(event.getSequenceNumber());
       if (index < 0) {
           Log.w(TAG, "Attempted to finish an input event that is not in progress.");
       } else {
          int seq = mSeqMap.valueAt(index);
          mSeqMap.removeAt(index);
          // 调用naive接口通知事件是否已经消费
          nativeFinishInputEvent(mReceiverPtr, seq, handled);
       }
       event.recycleIfNeededAfterDispatch();
}

6.4 android_view_InputEventReceiver

来到native层,nativeFinishInputEvent 实现如下

> frameworks/native/core/jni/android_view_InputEventReceiver.cpp

static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jlong receiverPtr,
        jint seq, jboolean handled) {
    sp<NativeInputEventReceiver> receiver = reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
    status_t status = receiver->finishInputEvent(seq, handled);
    .....
}

6.5 NativeInputEventReceiver

NativeInputEventReceiver::finishInputEvent 方法实现

> frameworks/native/core/jni/android_view_InputEventReceiver.cpp

status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {
    ....
    Finish finish{
            .seq = seq,
            .handled = handled,
    };
    mOutboundQueue.push_back(finish);// 将finish事件添加到集合的最后
    return processOutboundEvents();
}

status_t NativeInputEventReceiver::processOutboundEvents() {
    while (!mOutboundQueue.empty()) {
        OutboundEvent& outbound = *mOutboundQueue.begin();
        status_t status;
        if (std::holds_alternative<Finish>(outbound)) {
            const Finish& finish = std::get<Finish>(outbound);
            // mInputConsumer是InputConsumer的实例
            status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
        } else if (std::holds_alternative<Timeline>(outbound)) {
            const Timeline& timeline = std::get<Timeline>(outbound);
            status = mInputConsumer.sendTimeline(timeline.inputEventId, timeline.timeline);
        } 
        ....
        return status;
    }
    return OK;
}

mInputConsumer.sendFinishedSignal() 实现是在InputTransport.cpp 中

6.6 InputTransport

sendFinishedSignal 实现如下

> frameworks/native/libs/input/InputTransport.cpp
  
status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
    if (seqChainCount) {
        ....
        while (!status && chainIndex > 0) {
            chainIndex--;
            status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled);
        }
        ....
    }
    // 为批次中的最后一条消息发送完成信号。
    return sendUnchainedFinishedSignal(seq, handled);
}

主要在sendUnchainedFinishedSignal中,通过InputChannel 发送完成信号

> frameworks/native/libs/input/InputTransport.cpp
  
status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
    InputMessage msg;
    msg.header.type = InputMessage::Type::FINISHED;
    msg.header.seq = seq;
    msg.body.finished.handled = handled;
    msg.body.finished.consumeTime = getConsumeTime(seq);
    status_t result = mChannel->sendMessage(&msg);
    return result;
}

那这个message是在哪里接收到呢?

6.7 InputManagerService

createInputChannel 的实现如下

>  frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

   // 创建一个输入通道以用作输入事件目标。
    public InputChannel createInputChannel(String name) {
        // mNative是NativeInputManagerService 接口的实例,NativeImpl 实现了该接口,所以实现在NativeImpl 中
        return mNative.createInputChannel(name);
    }

createInputChannel 是一个native方法

>  frameworks/base/services/core/java/com/android/server/input/NativeInputManagerService.java
    class NativeImpl implements NativeInputManagerService {
       ....
       public native InputChannel createInputChannel(String name);  
    }

createInputChannel() 在 native层对应的方法是nativeCreateInputChannel

>  frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

// Java层的createInputChannel 方法映射到native的nativeCreateInputChannel 方法
static const JNINativeMethod gInputManagerMethods[] = {
		   ...
        {"createInputChannel", "(Ljava/lang/String;)Landroid/view/InputChannel;",
         (void*)nativeCreateInputChannel},  
}


int register_android_server_InputManager(JNIEnv* env) {
    // 注册native层的方法,与Java层的NativeInputManagerService$NativeImpl绑定
    int res = jniRegisterNativeMethods(env,
                                       "com/android/server/input/"
                                       "NativeInputManagerService$NativeImpl",
                                       gInputManagerMethods, NELEM(gInputManagerMethods));
}

static jobject nativeCreateInputChannel(JNIEnv* env, jobject nativeImplObj, jstring nameObj) {
    NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
    ....
    base::Result<std::unique_ptr<InputChannel>> inputChannel = im->createInputChannel(name);
    ...
    jobject inputChannelObj =
            android_view_InputChannel_createJavaObject(env, std::move(*inputChannel));
    android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
            handleInputChannelDisposed, im);
    return inputChannelObj;
}

// NativeInputManager的createInputChannel 方法定义
base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputChannel(
        const std::string& name) {
    // mInputManager 是 InputManager 的实例
    return mInputManager->getDispatcher().createInputChannel(name);
}

getDispatcher() 方法返回InputDispatcher实例

6.8 InputDispatcher

createInputChannel实现如下

> frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
  
Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
    std::unique_ptr<InputChannel> serverChannel;
    std::unique_ptr<InputChannel> clientChannel;
    // 将serverChannel(接收端) 与 clientChannel(发送端) 绑定
    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
    { // acquire lock
        std::scoped_lock _l(mLock);
        const sp<IBinder>& token = serverChannel->getConnectionToken();
        int fd = serverChannel->getFd();
        sp<Connection> connection =
                new Connection(std::move(serverChannel), false /*monitor*/, mIdGenerator);
      
        mConnectionsByToken.emplace(token, connection);
        // 注册回调 handleReceiveCallback 方法
        std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
                                                            this, std::placeholders::_1, token);
        // 将callback 与 FD(即serverChannel) 关联起来
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);
    } // release lock
    mLooper->wake();
    return clientChannel;
}

所以应用层发送的finish事件最终来到了InputDispatcher::handleReceiveCallback 方法

handleReceiveCallback实现如下

> frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp

int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionToken) {
    sp<Connection> connection = getConnectionLocked(connectionToken);
    bool notify;
    if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
        nsecs_t currentTime = now();
        bool gotOne = false;
        status_t status = OK;
        for (;;) {
            Result<InputPublisher::ConsumerResponse> result =
                    connection->inputPublisher.receiveConsumerResponse();
            if (!result.ok()) {
                status = result.error().code();
                break;
            }
            // 如果分发结束
            if (std::holds_alternative<InputPublisher::Finished>(*result)) {
                const InputPublisher::Finished& finish =
                        std::get<InputPublisher::Finished>(*result);
                // 执行事件分发结束的回收处理
                finishDispatchCycleLocked(currentTime, connection, finish.seq, finish.handled,
                                          finish.consumeTime);
            } else if (std::holds_alternative<InputPublisher::Timeline>(*result)) {
                if (shouldReportMetricsForConnection(*connection)) {
                    const InputPublisher::Timeline& timeline =
                            std::get<InputPublisher::Timeline>(*result);
                    mLatencyTracker
                            .trackGraphicsLatency(timeline.inputEventId,
                                                  connection->inputChannel->getConnectionToken(),
                                                  std::move(timeline.graphicsTimeline));
                }
            }
            gotOne = true;
        }
    }
    ....
    return 0; // remove the callback
}

finishDispatchCycleLocked实现如下

> frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
  
void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
                                                const sp<Connection>& connection, uint32_t seq,
                                                bool handled, nsecs_t consumeTime) {
    // 通知其他系统组件并准备开始下一个调度周期。
    auto command = [this, currentTime, connection, seq, handled, consumeTime]() REQUIRES(mLock) {
        doDispatchCycleFinishedCommand(currentTime, connection, seq, handled, consumeTime);
    };
    postCommandLocked(std::move(command));
}

doDispatchCycleFinishedCommand 该方法主要处理执行完成后的回收动作,包括将对应的事件从waitQueue 队列中移除。

> frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
  
void InputDispatcher::doDispatchCycleFinishedCommand(... uint32_t seq ..) {
    std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);
    // 将事件出列并开始下一个循环。因为锁可能已经被释放,等待队列的内容可能已经被清空,所以我们需要仔细检查一些事情。
    if (dispatchEntryIt != connection->waitQueue.end()) {
        dispatchEntry = *dispatchEntryIt;
        // 将对应的事件从waitQueue移除
        connection->waitQueue.erase(dispatchEntryIt);
        const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken();
        // 将事件对应的anr超时移除 
        mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);
        // 如果连接没有响应
        if (!connection->responsive) {
            connection->responsive = isConnectionResponsive(*connection);
            if (connection->responsive) {
                // 连接之前没有响应,现在有响应了。
                processConnectionResponsiveLocked(*connection);
            }
        }
        ....
    }
}

6.9 调用栈

frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp : InputDispatcher::doDispatchCycleFinishedCommand()
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp : InputDispatcher::finishDispatchCycleLocked()
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp : InputDispatcher::handleReceiveCallback()
frameworks/native/libs/input/InputTransport.cpp : InputConsumer::sendUnchainedFinishedSignal()
frameworks/native/libs/input/InputTransport.cpp : InputConsumer::sendFinishedSignal()
frameworks/native/core/jni/android_view_InputEventReceiver.cpp : NativeInputEventReceiver::processOutboundEvents()
frameworks/native/core/jni/android_view_InputEventReceiver.cpp : NativeInputEventReceiver::finishInputEvent()
frameworks/native/core/jni/android_view_InputEventReceiver.cpp : nativeFinishInputEvent()
frameworks/base/core/java/android/view/InputEventReceiver.java : InputEventReceiver.finishInputEvent()
frameworks/base/core/java/android/view/ViewRootImpl.java : ViewRootImpl.finishInputEvent()
frameworks/base/core/java/android/view/ViewRootImpl.java : InputStage.forward()
frameworks/base/core/java/android/view/ViewRootImpl.java : InputStage.onDeliverToNext()
frameworks/base/core/java/android/view/ViewRootImpl.java : SyntheticInputStage.apply()
frameworks/base/core/java/android/view/ViewRootImpl.java : ViewPostImeInputStage.apply()

总结

事件分发流程图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FoEWGzHh-1682737256424)(/img/blog_activity_dispatch_input_event/5.png)]

参考文章:

  • Input系统—事件处理全过程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/474222.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

创建NAT模式KVM虚拟机

创建NAT模式KVM虚拟机 1 添加脚本执行权限&#xff08;上传脚本文件至root目录&#xff09;。 首先需要给脚本赋予执行权限。 # chmod x qemu-ifup-NAT 2 启动虚拟机。 通过命令启动虚拟机。(记得安装net-tools) # yum install net-tools -y # qemu-kvm -m 1024 -drive fi…

注解-Annotation

一. 注解解析 1.1 注释和注解的区别&#xff1f;&#xff08;掌握&#xff09; 共同点&#xff1a;都可以对程序进行解释说明。不同点&#xff1a;注释&#xff0c;是给程序员看的。只在Java中有效。在class文件中不存在注释的。当编译之后&#xff0c;会进行注释擦除。 注释…

了解标量、向量和点积

数据科学基础数学&#xff1a;线性代数简介 了解标量、向量和点积 机器只能按着算法理解和处理数据结构存储的数字. 例如创建垃圾邮件检测器&#xff0c;则首先必须将文本数据转换为数字(通过单词嵌入)。 两个句子之间的余弦相似性 两个句子之间的余弦相似性可以通过它们的向量…

【软件相关】Proteus仿真STM32记录

文章目录 0 前期教程1 前言2 先说说建议的流程3 需要注意的事项3.1 供电网配置不要忘了3.2 ADC模块的使用3.3 元器件查询手册 4 一些小技巧 0 前期教程 【软件相关】Proteus 8入门教程 1 前言 最近接了一个项目&#xff0c;是基于Proteus仿真STM32的&#xff0c;虽然此前有听…

软件开发过程中,前期准备的重要性

需求人员&#xff0c;有时候&#xff0c;一开始&#xff0c;并不完全确定自己想要的是什么 因此&#xff0c;值得花费时间&#xff0c;找出他们真正想要的东西 因为&#xff0c;改动的代价&#xff0c;可能代价会是巨大的 构建之前清除一个错误的成本 ( 1 10 ∼ 1 100 ) 开发过…

Mysql 触发器 -> 监控 -> 表 修改前后的 数据 -> 日志

0. 课程视频 https://www.bilibili.com/video/BV1Kr4y1i7ru?p116&spm_id_frompageDriver&vd_sourceff8b7f852278821525f11666b36f180a 1. 简介 1.1 是对象->与表有关 1.2 校验数据完整性 -> 日志记录 1.3 在 insert / update /delete 前后触发 1.4 支持行…

权限提升:Mysql 数据库 .(UDF || 启动项 || 反弹)

权限提升&#xff1a;Mysql 数据库. 权限提升简称提权&#xff0c;由于操作系统都是多用户操作系统&#xff0c;用户之间都有权限控制&#xff0c;比如通过 Web 漏洞拿到的是 Web 进程的权限&#xff0c;往往 Web 服务都是以一个权限很低的账号启动的&#xff0c;因此通过 Web…

〖ChatGPT实践指南 - 零基础扫盲篇⑨〗- OpenAI API 的数据使用政策以及使用限制

文章目录 ⭐ OpenAI API 数据使用政策⭐ 关于 API 数据的一些通用型问题⭐ OpenAI API 数据使用政策的限制 该章节为各位小伙伴介绍以下 OpenAI 的API 数据使用政策&#xff0c;这部分的内容是 OpenAI 官方为我们提供的一份文档&#xff0c;这份文档说明了官方在为我们提供的AP…

Python小姿势 - ## 一、Python多进程编程

一、Python多进程编程 在上一节&#xff0c;我们学习了Python的多线程编程&#xff0c;这节我们学习一下Python的多进程编程。 多进程和多线程比较类似&#xff0c;区别在于多进程是指在一个应用程序中同时运行多个进程&#xff0c;而多线程是指在一个进程中同时运行多个线程。…

ovs-vsctl 命令详解

ovs-vsctl 命令详解 网桥Bridge 创建 Bridge ovs-vsctl add-br br0 删除 Bridge ovs-vsctl del-br br0 列出 Bridge ovs-vsctl list-br 显示详情 ovs-vsctl show 端口 Port 添加端口 ovs-vsctl add-port br0 p1 其中br0 为上面添加的bridge p1可以是物理端口或者vN…

50道web前端工程师面试题及答案解析,你学会了吗

简介&#xff1a;本文包含了50个实用的前端面试题及答案解析&#xff0c;涵盖了HTML、CSS、JavaScript、DOM、Ajax、MVC、模块化、ES6、SPA、Webpack、Babel、Virtual DOM、响应式设计、移动优先设计、响应式图片、CSS 预处理器、后处理器、模块化、布局、盒模型、浮动、定位、…

国民技术N32G430开发笔记(5)- 基本定时器TIM6的使用

基本定时器TIM6的使用 1、设置一个定时器&#xff0c;100ms一次去开关LED灯 2、tim6的初始化代码&#xff1a; 大部分代码来自官方demo /****************************************************************************** Copyright (c) 2019, Nations Technologies Inc.** …

python毕业设计之django+vue专业手语翻译工作室预约管理系统

发起预约&#xff1a;用户选定某翻译员发起预约&#xff0c;进行预约信息填写&#xff0c;主要内容有联系电话、工作方式、场合、时间段&#xff1b;或发起预约&#xff0c;填写预约信息&#xff0c;筛选翻译员&#xff0c;可按关键词筛选或等级筛选。发起预约时间需至少提前服…

从CPU的视角看 多线程代码为什么那么难写!

当我们提到多线程、并发的时候&#xff0c;我们就会回想起各种诡异的bug&#xff0c;比如各种线程安全问题甚至是应用崩溃&#xff0c;而且这些诡异的bug还很难复现。我们不禁发出了灵魂拷问 “为什么代码测试环境运行好好的&#xff0c;一上线就不行了&#xff1f;”。 为了解…

如何将 github pages 迁移到 vercel 上托管

如何将 github pages 迁移到 vercel 上托管 前言 早期网站使用 github pages,后来迁移到 coding,最近又放到腾讯云网站静态托管,无论是 coding 的 cos 存储桶,还是静态网站托管 他们都是收费的,那有没有免费的托管商呢,既不影响网站的访问速度还免费,于是,找了一下,还真有,ve…

电商广告营销中,有哪些常见公式和优化手段

对于电商广告ROI的计算&#xff0c;广泛地应用在二类电商、跨境电商等营销场景中&#xff0c;了解这些相关的投放公式&#xff0c;有助于我们更好地进行广告投放和优化。 特别是针对商家而言&#xff0c;可以指导选品及定价策略、优化最终的广告投放收益&#xff0c;避免出现花…

学系统集成项目管理工程师(中项)系列14_采购管理

1. 概念和术语 1.1. 采购是从项目团队外部获得产品、服务或成果的完整的购买过程 1.2. 三大类 1.2.1. 工程 1.2.2. 产品/货物 1.2.3. 服务 2. 主要过程 2.1. 编制采购管理计划 2.2. 实施采购 2.3. 控制采购 2.4. 结束采购 3. 合同 3.1. 包括买方和卖方之间的法律文…

css布局实战:动态详情九宫格

上篇文件介绍了如何实现九宫格图片展示:css布局入门级实战之九宫格网格布局.不过存在一个问题:图片之间没有间距,用户体验欠佳;基于以上问题,本文进行优化. 较之前实现样式做以下调整:四张图按照两行显示,每行显示三个.不足的显示空白.之前是两行两列显示. 对应九宫格样…

忆暖行动|“他一个人推着老式自行车在厚雪堆的道路上走,车上带着学生考试要用的司机”

忆暖行动|“他一个人推着老式自行车在厚雪堆的道路上走&#xff0c;车上带着学生考试要用的sj” 一头白发&#xff0c;满山青葱 在那斑驳的物件褶皱中&#xff0c;透过泛黄的相片&#xff0c;掩藏着岁月的冲刷和青葱的时光。曾经的青年早已经不复年轻&#xff0c;但是那份热爱…

从信息泄露到权限后台

声明&#xff1a;文中涉及到的技术和工具&#xff0c;仅供学习使用&#xff0c;禁止从事任何非法活动&#xff0c;如因此造成的直接或间接损失&#xff0c;均由使用者自行承担责任。 点点关注不迷路&#xff0c;每周不定时持续分享各种干货。 众亦信安&#xff0c;中意你啊&a…