InputReader流程梳理

事件分发机制简述

事件传递的整个流程是怎样的?
因为View是树形结构的,基于这样的结构,事件进行有序的分发。事件分发就是当有多个对象均可以处理同一请求的时候,将这些对象串联成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
事件收集之后最先传递给Activity,然后依次向下传递:
Activity->PhoneWindow->DecorView->ViewGroup->…->View
当触发一个touch事件时,事件首先被分发到Activity的dispatchTouchEvent()方法中,Activity会先将事件分发给Window处理,然后Window调用superDispatchTouchEvent()方法;之后PhoneWindow又会调用DecorViewsuperDispatchTouchEvent()方法。最后DecorView调用ViewGroup的dispatchTouchEvent()方法进行事件分发。就这样一步步分发到用户调用setContentView()传入的ViewGroup的dispatchTouchEvent()方法中。
ViewGroup的dispatchTouchEvent()方法让事件分发时,会先调用onInterceptTouchEvent()方法判断是否拦截事件,如果拦截则mFirstTouchTarget为null;如果不拦截就查找对应的子控件进行事件处理。最后不管是否找到处理它的子控件,都会调用dispatchTransformedTouchEvent()。
如果最终没有任何View消费掉事件,那么事件会按照反方向回传,最终传回给Activity,如果最后Activity也没有处理,本次事件就会被抛弃。

整体流程

status_t InputManager::start() {
    
    
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    //...
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    //...
    return OK;
}

InputManager启动了一个InputReaderThread和InputDispatcherThread来读取和分发输入消息,调用它们的run方法后,会进入threadLoop函数(只要threadLoop函数返回true,该函数就会循环执行)。因此接下来直接分析InputReaderThread的threadLoop函数,实现如下:

bool InputReaderThread::threadLoop() {
    
    
    mReader->loopOnce();
    return true;
}

loopOnce

void InputReader::loopOnce() {
    
    
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    Vector<InputDeviceInfo> inputDevices;
    //...
    //关键点1 通过EventHub获取事件列表
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
 
    {
    
     // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();
        if (count) {
    
    
            //关键点2 对事件进行 加工处理
            processEventsLocked(mEventBuffer, count);
        }
        //...
    } // release lock
 
    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
    
    
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
    //关键点3 发布事件到InputDispatcher中
    mQueuedListener->flush();
}

以InputReader::loopOnce()为起点,多指触摸为例
在这里插入图片描述

  1. 从EventHub中获取事件列表。
    事件分为两类:设备节点中读取的原始输入事件,输入设备可用性变化事件(设备事件)。
    获取输入事件的目的:从驱动中将input事件独取出来转换成 RawEvent事件,同时对于输入设备,抽象成一个EventHub中的Device结构体并存储。
    EventHub负责打开/dev/input/目录下的所有设备,然后为每一个设备创建一个Device,并把这个Device放入EventHub所定义的数组mDevice中。之后把这个设备纳入监视范围。接下来等待事件发生,一旦有事件发生,就从产生input_event事件的设备中读取出这些设备,把这些事件转化为RawEvent类型放入InputReader提供的事件数组中,之后返回。
struct RawEvent {
    
    
    // Time when the event happened
    nsecs_t when;
    // Time when the event was read by EventHub. Only populated for input events.
    // For other events (device added/removed/etc), this value is undefined and should not be read.
    nsecs_t readTime;
    int32_t deviceId;//产生事件的设备Id,由EventHub自行分配,InputReader根据它从EventHub中获取此设备的详细信息
    int32_t type;//事件的类型 
    int32_t code;//事件代码
    int32_t value;//事件值
};
  1. 通过processEventsLocked()对事件进行处理,数据加工。
    判断type类型,如果已经开始触摸,type值肯定会小于FIRST_SYNTHETIC_EVENT进入processEventsForDeviceLocked()方法开始加工数据,否则添加、删除或配置设备,这里以添加为例addDeviceLocked(),这里主要创建InputDevice设备,并根据class给device添加了各种能够支持的Mapper。最后添加到mDevices的Vector容器中。
    对于设备事件,此函数对根据设备的可用性加载或移除设备对应的配置信息。对于原始输入事件,则在进行转译、封装与加工后将结果存储到mQueuedListener中。

  2. 发布事件到InputDispatcher
    所有事件处理完毕后,调用mQueuedListener.flush()将所有暂存的输入事件一次性地交付给InputDispatcher。

猜你喜欢

转载自blog.csdn.net/yimelancholy/article/details/130469186
今日推荐