Android Input子系统 - 触控事件处理机制

目录

1 概述

2 触控事件的读取

3 触控事件的分发

4 寻找触控事件的目标窗口

5 触控事件发送到目标窗口

5.1 触控事件发送到目标窗口流程

5.2 应用APP与InputDispatcher的InputChannel注册与监听

5.2.1 创建客户端与服务端的InputChannel

5.2.2 注册服务端InputChannel到InputDispatcher

5.2.3 App客户端监听触控事件

6 目标窗口内部的事件传递机制

6.1 UI线程对触控事件的分发处理

6.2 View的触控事件分发机制

7 触控问题调试机制

8 总结


1 概述

用户手指点击按压屏幕后,屏幕触控驱动产生中断,Linux内核会将硬件产生的触控事件包装成Event放在设备的dev/input/目录下。此后Android系统需要解决以下几个问题以实现整个触控事件的分发处理:

  1. 如何从设备上读取触控事件?
  2. 读取到触控事件后该如何派发事件?
  3. 派发事件时如何找到接收事件的目标应用窗口?
  4. 找到目标应用窗口后如何将事件传递到目标窗口?
  5. 目标应用窗口内部中的事件如何处理?

下面将结合Android 11系统源码,通过分析回答这些问题来了解Android系统触控事件处理机制的全貌。

2 触控事件的读取

Android所有的input输入设备都会在/dev/input目录下生成对应的设备节点,一旦有任何输入事件产生,Linux内核便会将事件写到这些节点下,同时对于外部输入设备(鼠标、键盘等)的插拔还会引起这些节点的创建和删除。系统封装了一个叫EventHub的对象,它负责利用Linux的inotify和epoll机制监听/dev/input目录下的设备事件节点,通过EventHub的getEvents接口就可以监听并获取该事件

系统开机启动system_server进程时会创建启动InputManagerService核心服务,其中会先通过JNI调用创建InputManager对象,然后进一步新建一个InputReader对象;然后通过start方法为InputReader创建一个InputThread的Loop工作线程,这个线程的工作就是通过EventHub的getEvents监听读取Input事件。详细流程如下图所示:

Android Input子系统 - 触控事件处理机制_第1张图片

简要代码流程如下:

  /*frameworks/base/services/java/com/android/server/SystemServer.java*/
  private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
     ...
     // 1.先创建InputManagerService对象
     inputManager = new InputManagerService(context);
     ...
     // 2.start启动相关input线程
     inputManager.start();
     ...
  }

  /*frameworks/base/services/core/java/com/android/server/input/InputManagerService.java*/
   public InputManagerService(Context context) {
        ...
        // 1.JNI调用native接口完成InputManager初始化动作
        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
        ...
    }

    public void start() {
        // 2.JNI调用native接口启动InputManager工作线程
        nativeStart(mPtr);
        ...
    }

 /*frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp*/
 static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    ...
    // 创建NativeInputManager对象
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    ...
}

NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp& looper) :
        mLooper(looper), mInteractive(true) {
    ...
    // 1.创建InputManager对象
    mInputManager = new InputManager(this, this);
    ...
}

static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
    ...
    // 2.调用InputManager的start函数
    status_t result = im->getInputManager()->start();
    ...
}

/*frameworks/native/services/inputflinger/InputManager.cpp*/
InputManager::InputManager(
        const sp& readerPolicy,
        const sp& dispatcherPolicy) {
    // 创建InputDispatcher触控事件分发对象
    mDispatcher = createInputDispatcher(dispatcherPolicy);
    mClassifier = new InputClassifier(mDispatcher);
    // 1.createInputReader创建InputReader事件读取对象,间接持有InputDispatcher的引用,以便事件读取完成后通知其进行事件分发
    mReader = createInputReader(readerPolicy, mClassifier);
}

status_t InputManager::start() {
    // 启动InputDispatcher工作线程
    status_t result = mDispatcher->start();
    ...
    // 2.启动InputReader工作线程
    result = mReader->start();
    ...
    return OK;
}

/*frameworks/native/services/inputflinger/reader/InputReader.cpp*/
InputReader::InputReader(std::shared_ptr eventHub,
                         const sp& policy,
                         const sp& listener)
      : mContext(this),
        // 初始化EventHub对象
        mEventHub(eventHub),
        mPolicy(policy),
        mGlobalMetaState(0),
        mGeneration(1),
        mNextInputDeviceId(END_RESERVED_ID),
        mDisableVirtualKeysTimeout(LLONG_MIN),
        mNextTimeout(LLONG_MAX),
        mConfigurationChangesToRefresh(0) {
    // 持有InputDispatcher的引用
    mQueuedListener = new QueuedInputListener(listener);
    ...
}

status_t InputReader::start() {
    if (mThread) {
        return ALREADY_EXISTS;
    }
    // 创建名为“InputReader”的InputThread带loop工作线程,并在其中循环执行loopOnce函数
    mThread = std::make_unique(
            "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
  return OK;
}

void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    std::vector inputDevices;
    { 
    ...
    // 1\. 从EventHub中监听读取触控事件
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
    { 
       ...
        if (count) {
            // 2.处理读取到的触控事件
            processEventsLocked(mEventBuffer, count);
        }
        ...
     }
    ...
    // 3.mQueuedListener其实就是InputDispatcher对象,flush会通知唤醒InputDispatcher分发事件
    mQueuedListener->flush();
}

通过上面流程,输入事件就可以被读取,经过InputReader::processEventsLocked被初步封装成RawEvent,最后通知InputDispatcher分发事件,下节继续分析事件的分发。

3 触控事件的分发

从上一节的代码流程分析中可以看到:在新建InputManager对象的时候,不仅创建了事件读取对象InputReader,还创建了事件分发对象InputDispatcher。InputReader在事件读取完毕后,会交给InputDispatcher去执行事件分发的逻辑,而InputDispatcher也拥有自己的独立的工作线程。这样设计的好处在于符合单一职责的设计思想,且事件的读取与分发工作在各自的线程中,处理效率更高,避免出现事件丢失。整个架构模型参见业界大神的一张图:

Android Input子系统 - 触控事件处理机制_第2张图片

接上一节代码分析,InputReader::loopOnce函数处理中,在触控事件读取完成后,最后一行会调用 mQueuedListener->flush()。mQueuedListener本质上就是InputDispatcher,所以调用flush动作就是通知InputDispatcher进行事件分发。InputDispatcher和InputReader一样,内部也有一个名为“InputDispatcher”的InputThread的Loop工作线程,当触控事件到来时其被唤醒处理事件。此处流程如下图所示:

Android Input子系统 - 触控事件处理机制_第3张图片

简化的代码流程如下:

/*frameworks/native/services/inputflinger/InputListener.cpp*/
void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        // 触发调用notify
        args->notify(mInnerListener);
        delete args;
    }
    mArgsQueue.clear();
}

void NotifyMotionArgs::notify(const sp& listener) const {
    // 就是调用InputDispatcher的notifyMotion
    listener->notifyMotion(this);
}

/*frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp*/
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
    ...
    // 1.可以增加业务逻辑,在事件分发前做一些事情
    mPolicy->interceptMotionBeforeQueueing(args->displayId, args->eventTime, /*byref*/ policyFlags);
    ...
    bool needWake;
    { 
      ...
      if (shouldSendMotionToInputFilterLocked(args)) {
            ...
            // 2\. filterInputEvent此处可以过滤触控事件,不再分发
            if (!mPolicy->filterInputEvent(&event, policyFlags)) {
                return; // event was consumed by the filter
            }
        }
        ...
        // 3.将触控事件放入"iq"队列等待分发
        needWake = enqueueInboundEventLocked(newEntry);
    } // release lock
    ...
    if (needWake) {
        // 4.唤醒loop工作线程,触发执行一次dispatchOnce逻辑
        mLooper->wake();
    }
}

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    bool needWake = mInboundQueue.empty();
    mInboundQueue.push_back(entry);
    // 触控事件放入mInboundQueue队列后,并增加"iq"的systrace tag信息
    traceInboundQueueLengthLocked();
    ...
    return needWake;
}

void InputDispatcher::traceInboundQueueLengthLocked() {
    if (ATRACE_ENABLED()) {
        // mInboundQueue队列对应的systrace tag为“iq”
        ATRACE_INT("iq", mInboundQueue.size());
    }
}

void InputDispatcher::dispatchOnce() {
     nsecs_t nextWakeupTime = LONG_LONG_MAX;
    {
        ...
        if (!haveCommandsLocked()) {
            // 1.具体的事件分发逻辑封装在dispatchOnceInnerLocked中具体处理
            dispatchOnceInnerLocked(&nextWakeupTime);
        }
        ...
    } // release lock
    ...
    // 2.处理完本次事件分发逻辑后,loop工作线程进入休眠等待状态
    mLooper->pollOnce(timeoutMillis);
}

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    ...
    switch (mPendingEvent->type) {
        ...
        case EventEntry::Type::MOTION: {
            ...
            // 以Motion event屏幕触控事件类型为例,具体的事件分发处理逻辑封装在dispatchMotionLocked函数中
            done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
            break;
        }
    }
    ...
}

bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry,
                                           DropReason* dropReason, nsecs_t* nextWakeupTime) {
    ATRACE_CALL();
    ...
    if (isPointerEvent) {
        // 1\. findTouchedWindowTargetsLocked中找到具体接收处理此触控事件的目标窗口
        injectionResult =
                findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,
                                               &conflictingPointerActions);
    } else {
        ...
    }
    ...
    // 2\. 将此次触控事件分发给目标应用窗口
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

从以上代码可以看出,对于触控事件的处理核心逻辑分为两步:

  1. 首先通过findTouchedWindowTargetsLocked找到处理此次触控事件的目标应用窗口;
  2. 下来通过dispatchEventLocked将触控事件发送到目标窗口。

下面将分两节来分别分析讲解这两个步骤。

4 寻找触控事件的目标窗口

Android系统的Primary Display主显示屏上同一时间总是会存在多个可见窗口,如状态栏、导航栏、应用窗口、应用界面Dialog弹

你可能感兴趣的:(Input子系统,android,java,开发语言)