Android触控事件处理机制(基于Android 11)

Posted 冬天的毛毛雨

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android触控事件处理机制(基于Android 11)相关的知识,希望对你有一定的参考价值。

作者:安卓M豆先生

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事件。详细流程如下图所示:

简要代码流程如下:

  /*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>& 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<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& 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<EventHubInterface> eventHub,
                         const sp<InputReaderPolicyInterface>& policy,
                         const sp<InputListenerInterface>& 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<InputThread>(
            "InputReader", [this]()  loopOnce(); , [this]()  mEventHub->wake(); );
  return OK;


void InputReader::loopOnce() 
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    std::vector<InputDeviceInfo> 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也拥有自己的独立的工作线程。这样设计的好处在于符合单一职责的设计思想,且事件的读取与分发工作在各自的线程中,处理效率更高,避免出现事件丢失。整个架构模型参见业界大神的一张图:

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

简化的代码流程如下:

/*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<InputListenerInterface>& 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弹框窗口等。用adb shell dumpsys SurfaceFlinger命令可以看到:

Display 19260578257609346 HWC layers:
-----------------------------------------------------------------------------------------------------------------------------------------------
 Layer name
           Z |  Window Type |  Layer Class |  Comp Type |  Transform |   Disp Frame (LTRB) |          Source Crop (LTRB) |     Frame Rate (Explicit) [Focused]
-----------------------------------------------------------------------------------------------------------------------------------------------
 com.android.systemui.ImageWallpaper#0
  rel      0 |         2013 |            0 |     DEVICE |          0 |    0    0 1080 2400 |    0.0    0.0 1080.0 2400.0 |                              [ ]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 cn.nubia.launcher/com.android.launcher3.Launcher#0
  rel      0 |            1 |            0 |     DEVICE |          0 |    0    0 1080 2400 |    0.0    0.0 1080.0 2400.0 |                              [*]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 FloatBar#0
  rel      0 |         2038 |            0 |     DEVICE |          0 | 1065  423 1080  623 |    0.0    0.0   15.0  200.0 |                              [ ]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 StatusBar#0
  rel      0 |         2000 |            0 |     DEVICE |          0 |    0    0 1080  111 |    0.0    0.0 1080.0  111.0 |                              [ ]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Android事件处理

Android_基于监听的事件处理机制

Android 基于监听的事件处理机制详解

Android事件处理机制

3.1.1 基于监听的事件处理机制

android的事件处理机制有两种