Android Input Framework(三)---InputReader&InputDispatcher

本文详细解析了Android系统中触摸事件的处理流程,从InputReader读取原始事件开始,到最终分发给应用程序的过程。重点关注了触摸屏消息处理,包括设备添加、删除事件处理以及多点触摸消息的具体实现。

1InputReader处理Input消息

InputReaderThread继承于Thread中,读取RawEvent数据流程如下:

1)        Thread::_threadLoop()

2)        InputReaderThread::threadLoop()

3)        InputReader::loopOnce()

4)        EventHub::getEvents()

     InputReader::loopOnce中,当调用EventHub->getEvents获取Input事件,当没有Input事件的时候,线程回阻塞,直到有输入事件,读取到到RawEvent之后,调用InputReader::processEventsLocked来处理这些事件,然后调用mQueuedListener->flush()把这些队列中的事件发送到InputDispatcher。下面看处理processEventsLocked(const RawEvent* rawEvents, size_tcount)方法,在该方法中处理两种事件:

n         处理设备增加、删除和修改事件

n         处理来自于事件驱动设备的事件(processEventsForDeviceLocked

 

RawEvent结构体中有一个type变量,用这个变量来识别到底是什么事件:

n         EventHubInterface::DEVICE_ADDED   添加设备事件

n         EventHubInterface::DEVICE_REMOVED  删除设备事件

n         EventHubInterface::FINISHED_DEVICE_SCAN   完成设备扫面事件

n         小于EventHubInterface::DEVICE_ADDED    设备输入事件,包括按键、触摸等

代码如下:

voidInputReader::processEventsLocked(const RawEvent* rawEvents, size_tcount) {

    for(const RawEvent* rawEvent = rawEvents; count;) {

        int32_ttype = rawEvent->type;

        size_tbatchSize = 1;

        if(type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {

            int32_tdeviceId = rawEvent->deviceId;

            while(batchSize < count) {

                if(rawEvent[batchSize].type >=EventHubInterface::FIRST_SYNTHETIC_EVENT

                        ||rawEvent[batchSize].deviceId != deviceId) {

                    break;

                }

                batchSize+= 1;

            }

            processEventsForDeviceLocked(deviceId,rawEvent, batchSize);

        }else {

            switch(rawEvent->type) {

            caseEventHubInterface::DEVICE_ADDED:

                addDeviceLocked(rawEvent->when,rawEvent->deviceId);

                break;

            caseEventHubInterface::DEVICE_REMOVED:

                removeDeviceLocked(rawEvent->when,rawEvent->deviceId);

                break;

            caseEventHubInterface::FINISHED_DEVICE_SCAN:

                handleConfigurationChangedLocked(rawEvent->when);

                break;

            default:

                ALOG_ASSERT(false);// can't happen

                break;

            }

        }

        count-= batchSize;

        rawEvent+= batchSize;

    }

}

 

1.1 处理设备增加、删除和修改事件

1.1.1 增加设备

voidInputReader::addDeviceLocked(nsecs_t when, int32_t deviceId){

    ssize_tdeviceIndex = mDevices.indexOfKey(deviceId);

    InputDeviceIdentifieridentifier =mEventHub->getDeviceIdentifier(deviceId);

    uint32_tclasses = mEventHub->getDeviceClasses(deviceId);

    InputDevice*device = createDeviceLocked(deviceId, identifier,classes);

    mDevices.add(deviceId,device);

    bumpGenerationLocked();

}

调用createDeviceLocked()创建了一个InputDevice对象,并添加到mDevices列表中。下面看看createDeviceLocked是如何创建设备的。

先根据mContext, deviceId, name, classes创建一个InputDevice对象,它用于表示单个输入设备的状态。其中的classes为对应Deviceclasses成员,它用于表示设备类型,定义如下:

enum{

    

    INPUT_DEVICE_CLASS_KEYBOARD      =0x00000001,

 

    

    INPUT_DEVICE_CLASS_ALPHAKEY      =0x00000002,

 

    

    INPUT_DEVICE_CLASS_TOUCH         =0x00000004,

 

    

    INPUT_DEVICE_CLASS_CURSOR        =0x00000008,

 

    

    INPUT_DEVICE_CLASS_TOUCH_MT      =0x00000010,

 

    

    INPUT_DEVICE_CLASS_DPAD          =0x00000020,

 

    

    INPUT_DEVICE_CLASS_GAMEPAD       =0x00000040,

 

    

    INPUT_DEVICE_CLASS_SWITCH        =0x00000080,

 

    

    INPUT_DEVICE_CLASS_JOYSTICK      =0x00000100,

 

    

    INPUT_DEVICE_CLASS_VIBRATOR      =0x00000200,

 

    

    INPUT_DEVICE_CLASS_VIRTUAL       =0x40000000,

 

    

    INPUT_DEVICE_CLASS_EXTERNAL      =0x80000000,

};

创建InputDevice对象之后,会根据classs类型,创建对应的InputManager对象,InputMapper对象主要负责将输入的原始数据进行处理,使其转换成标准的输入事件,每一个InputDevice至少有一个InputMapper,类图关系如下:

Android <wbr>Input <wbr>Framework(三)---InputReader&InputDispatcher

这张图,我们关注下mContext变量,InputReader::mContext在构造时用自己的指针初始化了mContext,从而mContext::mReader则为此InputReader实例。在InputReader::createDeviceLocked中创建InputDevice时,把自己的mContext作为参数传入,从而把它保存在InputDevice::mContext中;在创建InputMapper时,以InputDevice作为参数,且InputMapper把它保存在mDevice中,然后从把InputDevice中的mContext也保存在InputMappermContext中。

这里我们主要关注的是触摸屏消息:

INPUT_DEVICE_CLASS_TOUCH_MT   多点触摸屏

INPUT_DEVICE_CLASS_TOUCH       单点触摸屏

//Touchscreens and touchpad devices.

    if(classes & INPUT_DEVICE_CLASS_TOUCH_MT) {

        device->addMapper(newMultiTouchInputMapper(device));

    }else if (classes & INPUT_DEVICE_CLASS_TOUCH) {

        device->addMapper(newSingleTouchInputMapper(device));

    }

1.2触摸屏消息处理

1.2.1时序图(触摸消息)

Android <wbr>Input <wbr>Framework(三)---InputReader&InputDispatcher

1.2.2 消息处理

关注下两个变量:

n         mContext变量

InputReader::mContext在构造时用自己的指针初始化了mContext,从而mContext::mReader则为此InputReader实例。在InputReader::createDeviceLocked中创建InputDevice时,把自己的mContext作为参数传入,从而把它保存在InputDevice::mContext中;在创建InputMapper时,以InputDevice作为参数,且InputMapper把它保存在mDevice中,然后从把InputDevice中的mContext也保存在InputMappermContext中。

n         mQueuedListener变量

该变量是在InputReader构造函数中创建的QueuedListener对象,在QueuedListener的构造函数中,将InputDispatcher的实例作为参数赋给内部变量mInnerListener

mContext->getListener()返回的就是mQueuedListener

 

分析RawEvent结构体数据,

n         RawEvent->type表示输入事件类型:

#define  EV_SYN   0x00    //为结束标志
#define  EV_KEY   0x01    //
按键事件

#define  EV_REL    0x02    //相对坐标,如光标移动,报告的是相对最后一次位置的偏移
#define  EV_ABS    0x03    //
绝对坐标   //如触摸屏和操纵杆,它们工作在绝对坐标系统

 

 

n         RawEvent->code表示数据类型:

#define  ABS_MT_POSITION_X  0x35  //表示x坐标

#define  ABS_MT_POSITION_Y  0x36   //表示y坐标

#define  ABS_MT_TOUCH_MAJOR  0x30  //接触面的长轴。

#define  ABS_MT_TOUCH_MINOR  0x31  //接触面的短轴,

#define  ABS_MT_WIDTH_MAJOR  0x32  //接触工具的长轴

#define  ABS_MT_WIDTH_MINOR  0x33   //接触工具的切面的短轴

#define  ABS_MT_PRESSURE  0x 3a //接触工具对接触面的压力大小,可以用来代替上面的四个参数。

#define  ABS_MT_ORIENTATION    0x34

#define  ABS_MT_TRACKING_ID    0x39  //表示当前多少个手指触摸

n         RawEvent->value 表示数据的值

 

对于多点触摸,消息发送方式如下:

ABS_MT_POSITION_X

ABS_MT_POSITION_Y

ABS_MT_TRACKING_ID

ABS_MT_TOUCH_MAJOR

ABS_MT_WIDTH_MAJOR

SYN_MT_REPORT

……

……

ABS_MT_POSITION_X

ABS_MT_POSITION_Y

ABS_MT_TRACKING_ID

ABS_MT_TOUCH_MAJOR

ABS_MT_WIDTH_MAJOR

SYN_MT_REPORT

SYN_REPORT

其中ABS_MT_POSITION_X\Y表示触摸消息的坐标值,ABS_MT_TRACKING_ID表示目前数据是几个手指触摸,SYN_MT_REPORT表示某个触摸信息已经上报完,SYN_REPORT表示一笔完整的多点触摸消息已经上报完成。

 

对于多点触摸消息,将在MultiTouchInputMapper对消息处理,看看process方法:

voidMultiTouchInputMapper::process(const RawEvent* rawEvent){

    TouchInputMapper::process(rawEvent);

    mMultiTouchMotionAccumulator.process(rawEvent);

}

voidTouchInputMapper::process(const RawEvent* rawEvent) {

    mCursorButtonAccumulator.process(rawEvent);

    mCursorScrollAccumulator.process(rawEvent);

    mTouchButtonAccumulator.process(rawEvent);

    //一个事件结束的标志

    if(rawEvent->type == EV_SYN && rawEvent->code ==SYN_REPORT) {

        sync(rawEvent->when);

    }

}

mCursorButtonAccumulator处理EV_KEY类型输入事件

mCursorScrollAccumulator处理EV_REL类型输入事件

mTouchButtonAccumulator处理EV_KEY类型输入事件

mMultiTouchMotionAccumulator处理EV_ABS类型输入事件,也就是触摸输入事件,当满足rawEvent->type == EV_SYN &&rawEvent->code == SYN_REPORT条件时,说明一个事件已经完成上报,开始同步。

n         sync(rawEvent->when)

      处理EV_SYN:SYN_REPORT,我们的EV_SYN就在这儿被处理了,当然它是Touch Down时,所发事件的最后一个事件。这儿才是处理的重点。TouchInputMapper::sync将调用MultiTouchInputMapper::syncTouch函数。

n         MultiTouchInputMapper::syncTouch

           mMultiTouchMotionAccumulator读取的数据存在mCurrentRawPointerData.pointers中,单点触摸的syncTouch一次处理一个RawEvent,在pointers中只有一个值;而多点触摸的syncTouch一次处理多个RawEvent,在pointers中有多个值,最多16个。

n         TouchInputMapper::cookPointerData

      根据TouchInputMapper::mCurrentRawPointerData->pointers中的数据,通过计算,最后生成TouchInputMapper::mCurrentCookedPointerData.pointerCoordsmCurrentCookedPointerData.pointerPropertiesmCurrentCookedPointerData.idToIndex的数据。把Raw进行cook,之后生成了cooked数据。

n         TouchInputMapper::dispatchTouches

调用dispatchMotion

n         TouchInputMapper::dispatchMotion

    根据cooked数据创建NotifyMotionArg对象,它描述了一个移动事件。

n         调用TouchInputMapper::getListener()->notifyMotion(&args)

        TouchInputMapper::getListener()调用mContext->getListener(),此mContextInputReader::mContext,所以其getListener()返回的则为InputReader::mQueuedListener,则最后调用QueuedInputListener::notifyMotion

n         mArgsQueue.push(newNotifyMotionArgs(*args))

把传递过来的NotifyMotionArg参数复制一份,然后加入QueuedInputListener::mArgsQueue例表中。

 

此时触摸事件的数据已经都放在了mArgsQueue列表中,在InputReader::LoopOnce中,调用mQueuedListener->flush()开始把数据分发到InputDispatcher中,mInnerListener就是InputDispatcher的实例。

voidQueuedInputListener::flush() {

    size_tcount = mArgsQueue.size();

    for(size_t i = 0; i < count; i++) {

        NotifyArgs*args = mArgsQueue[i];

        args->notify(mInnerListener);

        deleteargs;

    }

    mArgsQueue.clear();

}

调用链表中每个NotifyArgsnotify函数这么多类NotifyArgs,为描述方便,下面以NotifyMotionArgs为例,其代码为: 

voidNotifyMotionArgs::notify(const sp& listener) const {

 listener->notifyMotion(this);

}

1.2.3消息结构变化流程

   Android <wbr>Input <wbr>Framework(三)---InputReader&InputDispatcher


2InputDispatcher 消息分发

InputDispatcherThread开启了一个线程来接收InputReader发送过来的消息,当没有消息的时候,线程进行了休眠,当有消息的时候,mLooer->wake()唤醒线程。InputDispatcherThread主循环如下:

1)        Thread::_threadLoop()

2)        InputDispatcherThread::threadLoop()

3)        InputDispatcher::dispatchOnce()

4)        dispatchOnceInnerLocked()

分发消息

5)        mLooper->pollOnce()

    其功能为等待超时或被pipe唤醒(InputReader线程调用InputDispatcher::notifyMotion时, InputDispatcher::notifyMotion根据情况调用mLooper->wake)

2.1 唤醒线程

     经过第4节的介绍,以触摸消息为例,InputReaderNotifyMotionArgs结构体分发到了InputDispatcher

voidInputDispatcher::notifyMotion(const NotifyMotionArgs* args){

        ……

        //Just enqueue a new motion event.

        MotionEntry*newEntry = newMotionEntry(args->eventTime,

                args->deviceId,args->source, policyFlags,

                args->action,args->flags, args->metaState,args->buttonState,

                args->edgeFlags,args->xPrecision, args->yPrecision,args->downTime,

                args->pointerCount,args->pointerProperties, args->pointerCoords);

 

        needWake= enqueueInboundEventLocked(newEntry);

    ……

    if(needWake) {

        mLooper->wake();

    }

}

n         根据NotifyMotionArgs提供的信息,构造一个MotionEvent

n         调用InputDispatcher::enqueueInboundEventLocked把新构造的MotionEntry添加到InputDispatcher::mInboundQueue中,并返回是否需要唤醒mLooper<</span>pipe中写入数据>的标识。

      以上操作都是在InputReader线程中完成的,现在应该InputDispatcher线程开始工作了。

2.2InputDispatcher线程工作

5.1中,调用了mLooper->wake()唤醒InputDispatcher线程,分发工作主要是在dispatchOnceInnerLocked方法中。

n         mInboundQueue从中依次取出EventEntry的基类>

n         调用InputDispatcher::dispatchMotionLocked处理此MotionEntry

n         调用InputDispatcher::dispatchEventLocked

      获取窗口对应的InputTarget,并获取对应的Connection,调用InputDispatcher::prepareDispatchCycleLocked()

n         InputDispatcher::prepareDispatchCycleLocked

n         调用enqueueDispatchEntryLocked创建DispatchEntry对象,并把它增加到Connection::outboundQueue队列中。

n         调用InputDispatcher::startDispatchCycleLocked,接着它调用Connection::inputPublisher.publishMotionEvent来发布事件到ashmem buffer中,调用Connection::inputPublisher.sendDispatchSignal发送一个dispatch信号到InputConsumer通知它有一个新的消息到了,快来取消息吧

<think>好的,用户想了解Android框架中与输入相关的组件或实现。首先,我需要回忆一下Android输入系统的基本结构。Android的输入处理涉及到多个层次,从硬件驱动到应用层的交互,中间需要经过InputReader、InputDispatcher、WindowManagerService等组件。 用户提供的引用中提到了振动模块的实现,比如Context.java中的Vibrator服务,这可能与输入反馈有关,比如触觉反馈。不过用户的问题集中在输入组件,所以需要重点放在输入事件的处理流程。 首先,我应该从输入设备的驱动开始。Linux内核处理输入设备的原始事件,然后通过EventHub在用户空间读取这些事件。接着,InputReader会解析这些事件,并将其转换为Android的输入事件,比如触摸或按键事件。InputDispatcher负责将这些事件分发给合适的窗口或应用。 需要提到的关键类包括InputManagerService,它是输入系统的核心,管理InputReader和InputDispatcher的线程。WindowManagerService在确定哪个窗口应该接收事件时起重要作用,因为它处理窗口的层级和焦点状态。 另外,View和ViewRootImpl在应用层处理输入事件的分发,比如触摸事件的分发流程:从DecorView开始,经过ViewGroup,最终到达具体的View。MotionEvent类封装了触摸事件的信息,如坐标、动作类型等。 用户可能还想知道输入事件的处理流程,从硬件到应用的整个过程,以及涉及的IPC机制,比如InputChannel和Binder通信。另外,可能涉及到输入子系统的扩展,比如自定义输入设备或手势识别,这需要修改或扩展Framework层的相关组件。 需要确保回答中提到的组件和流程与用户提供的引用中的内容有联系。例如,振动服务可能在输入事件处理中作为反馈机制,但用户的问题主要是输入实现,所以重点不在振动,但可以提到触觉反馈作为输入处理的一部分。 最后,生成相关问题需要考虑用户可能进一步想了解的内容,比如输入事件的分发机制、性能优化、调试工具等。确保问题覆盖实现细节、流程和实际应用场景。</think>### Android框架输入系统实现解析 Android输入系统采用分层架构处理触摸屏、物理按键、传感器等输入设备事件,核心实现集中在$frameworks/base/services/core/java/com/android/server/input/$路径下,主要包含以下关键组件: 1. **输入事件处理流程** ``` 硬件驱动 -> EventHub -> InputReader -> InputDispatcher -> WindowManagerService -> ViewRootImpl -> Activity ``` 输入事件首先通过Linux内核的$evdev$驱动生成原始数据,经EventHub转换为标准格式[^2]。InputReader线程通过$epoll$机制读取事件并进行坐标转换,生成$KeyEvent$/$MotionEvent$对象。 2. **核心服务组件** - **InputManagerService(IMS)** 作为系统服务的入口,管理两个核心线程: ```java public class InputManagerService extends IInputManager.Stub { final InputReaderThread mReaderThread; final InputDispatcherThread mDispatcherThread; } ``` 负责初始化$EventHub$和设备监控,处理输入策略配置[^1] - **InputDispatcher** 实现事件分发逻辑,使用$InputChannel$与窗口建立IPC连接: ```cpp status_t InputDispatcher::dispatchMotion(...) { // 通过ANR检测机制保证响应时效性 checkWindowReadyForMoreInputLocked(...); // 使用BitTube进行跨进程数据传输 connection->inputPublisher.dispatchMotion(...); } ``` 3. **窗口级处理** WindowManagerService通过$InputMonitor$维护窗口堆栈状态,确定事件目标窗口。ViewRootImpl通过$ViewPostImeInputStage$处理输入事件的分发逻辑: ```java class ViewPostImeInputStage extends InputStage { protected int onProcess(...) { if (event.isTouchEvent()) { // 触发View树的onTouchEvent处理 handled = mView.dispatchPointerEvent(event); } } } ``` 4. **输入子系统扩展** 添加自定义输入设备时需扩展$InputDeviceMapper$,实现设备特征识别和事件转换。触觉反馈的实现可见振动服务$VibratorService$与输入事件的联动[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值