Android Input Framework-InputReader&InputDispatcher

来源:互联网 发布:directx for windows 编辑:程序博客网 时间:2024/06/05 23:44


原文转自http://blog.sina.com.cn/s/blog_89f592f501013969.html 

1 InputReader处理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_t count)方法,在该方法中处理两种事件:

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

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

 

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

n         EventHubInterface::DEVICE_ADDED   添加设备事件

n         EventHubInterface::DEVICE_REMOVED  删除设备事件

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

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

代码如下:

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {

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

        int32_t type = rawEvent->type;

        size_t batchSize = 1;

        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {

            int32_t deviceId = 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) {

            case EventHubInterface::DEVICE_ADDED:

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

                break;

            case EventHubInterface::DEVICE_REMOVED:

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

                break;

            case EventHubInterface::FINISHED_DEVICE_SCAN:

                handleConfigurationChangedLocked(rawEvent->when);

                break;

            default:

                ALOG_ASSERT(false); // can't happen

                break;

            }

        }

        count -= batchSize;

        rawEvent += batchSize;

    }

}

 

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

1.1.1 增加设备

void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {

    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);

    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);

    uint32_t classes = 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(new MultiTouchInputMapper(device));

    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {

        device->addMapper(new SingleTouchInputMapper(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方法:

void MultiTouchInputMapper::process(const RawEvent* rawEvent) {

    TouchInputMapper::process(rawEvent);

    mMultiTouchMotionAccumulator.process(rawEvent);

}

void TouchInputMapper::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(new NotifyMotionArgs(*args))

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

 

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

void QueuedInputListener::flush() {

    size_t count = mArgsQueue.size();

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

        NotifyArgs* args = mArgsQueue[i];

        args->notify(mInnerListener);

        delete args;

    }

    mArgsQueue.clear();

}

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

void NotifyMotionArgs::notify(const sp& listener) const {

 listener->notifyMotion(this);

}

1.2.3消息结构变化流程

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


2 InputDispatcher 消息分发

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

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {

        ……

        // Just enqueue a new motion event.

        MotionEntry* newEntry = new MotionEntry(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.2 InputDispatcher线程工作

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通知它有一个新的消息到了,快来取消息吧

阅读全文
2 0