了解更多,移步Android触摸事件传递机制系列详解

在Android触摸事件的传递(二)--输入系统InputManagerService,介绍IMS服务的启动过程会创建两个native线程,分别是InputReader,InputDispatcher. 接下来从InputReader线程的执行过程从threadLoop为起点开始分析

1 threadLoop

[-> InputReader.cpp]

bool InputReaderThread::threadLoop() {    mReader->loopOnce(); //【见小节1.2】    return true;}
  • threadLoop返回值true代表的是会不断地循环调用loopOnce()。另外,如果当返回值为false则会 退出循环。
  • 整个过程是不断循环的地调用InputReaderloopOnce()方法。

2 loopOnce();

  1. 查看InputReader配置是否修改,如界面大小、方向、键盘布局重新加载、指针速度改变等
  2. 从EventHub读取事件,其中EVENT_BUFFER_SIZE = 256
  3. 处理事件
  4. 将事件传到InputDispatcher

注:通过EventHub->getEvents(),获取输入事件和设备增删事件,count为读取的事件数量,mEventBuffer存储着事件。由上一篇文章可知getEvents()是阻塞的,只有当有事件或者被wake才会被唤醒向下执行。

void InputReader::loopOnce() {    int32_t oldGeneration;    int32_t timeoutMillis;    bool inputDevicesChanged = false;    Vector inputDevices;    { // acquire lock        AutoMutex _l(mLock);         oldGeneration = mGeneration;        timeoutMillis = -1;    //查看InputReader配置是否修改,如界面大小、方向、键盘布局重新加载、指针速度改变等        uint32_t changes = mConfigurationChangesToRefresh;        if (changes) {            mConfigurationChangesToRefresh = 0;            timeoutMillis = 0;            refreshConfigurationLocked(changes); //刷新配置         } else if (mNextTimeout != LLONG_MAX) {            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);            timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);        }    } // release lock    ////从EventHub读取事件,其中EVENT_BUFFER_SIZE = 256【见小节2.1】    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);     { // acquire lock        AutoMutex _l(mLock);        mReaderIsAliveCondition.broadcast();         if (count) {//处理事件【见小节3.1】            processEventsLocked(mEventBuffer, count);        }         if (mNextTimeout != LLONG_MAX) {            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);            if (now >= mNextTimeout) {                mNextTimeout = LLONG_MAX;                timeoutExpiredLocked(now);            }        }         if (oldGeneration != mGeneration) {            inputDevicesChanged = true;            getInputDevicesLocked(inputDevices);        }    } // release lock     // 发送一个消息,该消息描述已更改的输入设备。    if (inputDevicesChanged) {        mPolicy->notifyInputDevicesChanged(inputDevices);    }    mQueuedListener->flush();  //将事件传到InputDispatcher.}

2.1 EventHub->getEvents(),获取输入事件和设备增删事件

通过EventHub->getEvents(),获取输入事件和设备增删事件,count为读取的事件数量,mEventBuffer存储着事件。由上一篇文章可知getEvents()是阻塞的,只有当有事件或者被wake才会被唤醒向下执行。
Android触摸事件的传递(三)--输入系统EventHub

2.2 processEventsLocked 处理事件

  • 接着判断count的值,如果有读到事件(count大于0),则调用
    processEventsLocked(mEventBuffer, count)处理事件。
  • processEventsLocked()函数中会遍历所有的事件,分别进行处理。其处理的事件类型分为四种:原始输入事件、设备加载事件、设备卸载事件及FINISHED_DEVICE_SCAN事件。
  • 这里我们只关心对于设备自身产生的事件。也就是触摸屏相关的事件。也就是processEventsForDeviceLocked函数中所进行的操作。

[-> InputReader.cpp]

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; //同一设备的事件打包处理            }            //数据事件的处理【见小节3.3】            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);//不会发生                break;            }        }        count -= batchSize;  //count减少已处理事件个数,表示剩余事件个数        rawEvent += batchSize;  //rawEvent指针向后移动batchSize个RawEvent对象,也就是指到该处理的事件上。    }}

2.2.1事件派发到Device

根据事件获得相应的设备类型,然后将事件交给相应的设备处理。判断是否忽略该事件,如果不是忽略该事件,则会调用相应设备的process方法进行处理。

void InputReader::processEventsForDeviceLocked(int32_t deviceId,        const RawEvent* rawEvents, size_t count) {    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);    if (deviceIndex < 0) {        return;    }    InputDevice* device = mDevices.valueAt(deviceIndex);    if (device->isIgnored()) {        return;//可忽略则直接返回    }    device->process(rawEvents, count);}

2.2.3 事件派发到InputMapper

这里的事件又交给了InputMapper来处理

void InputDevice::process(const RawEvent* rawEvents, size_t count) {     ....    for (size_t i = 0; i < numMappers; i++) {           InputMapper* mapper = mMappers[i];            //调用具体mapper来处理           mapper->process(rawEvent);    }    ....}
Android触摸事件的传递(四-1)--输入系统-InputReader_第1张图片 160fe0a1c21df76b.png
  • InputMapper对应了很多的子类,这里根据事件的类型进行相应的派发,处理。
  • 事件到了这里之后,如何传递到应用层,这里mapper->process进行了那些处理。
  • 这里来看一下对于触摸屏事件的处理函数。
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);    }}

追踪数据流向
通过process函数处理,我们继续追踪函数的数据流向。对于相关的事件处理,这里最终执行的是将

void TouchInputMapper::sync(nsecs_t when) {    .....    processRawTouches(false /*timeout*/);}
void TouchInputMapper::processRawTouches(bool timeout) {    if (mDeviceMode == DEVICE_MODE_DISABLED) {        // Drop all input if the device is disabled.        mCurrentRawState.clear();        mRawStatesPending.clear();        return;    }    // Drain any pending touch states. The invariant here is that the mCurrentRawState is always    // valid and must go through the full cook and dispatch cycle. This ensures that anything    // touching the current state will only observe the events that have been dispatched to the    // rest of the pipeline.    const size_t N = mRawStatesPending.size();    size_t count;    for(count = 0; count < N; count++) {        const RawState& next = mRawStatesPending[count];        // A failure to assign the stylus id means that we're waiting on stylus data        // and so should defer the rest of the pipeline.        if (assignExternalStylusId(next, timeout)) {            break;        }        // All ready to go.        clearStylusDataPendingFlags();        mCurrentRawState.copyFrom(next);        if (mCurrentRawState.when < mLastRawState.when) {            mCurrentRawState.when = mLastRawState.when;        }        cookAndDispatch(mCurrentRawState.when);    }    if (count != 0) {        mRawStatesPending.removeItemsAt(0, count);    }    if (mExternalStylusDataPending) {        if (timeout) {            nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY;            clearStylusDataPendingFlags();            mCurrentRawState.copyFrom(mLastRawState);            cookAndDispatch(when);        } else if (mExternalStylusFusionTimeout == LLONG_MAX) {            mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT;            getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);        }    }}
void TouchInputMapper::cookAndDispatch(nsecs_t when) {    // Always start with a clean state.    mCurrentCookedState.clear();    // Apply stylus buttons to current raw state.    applyExternalStylusButtonState(when);    // Handle policy on initial down or hover events.    bool initialDown = mLastRawState.rawPointerData.pointerCount == 0            && mCurrentRawState.rawPointerData.pointerCount != 0;    uint32_t policyFlags = 0;    bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState;    if (initialDown || buttonsPressed) {        // If this is a touch screen, hide the pointer on an initial down.        if (mDeviceMode == DEVICE_MODE_DIRECT) {            getContext()->fadePointer();        }        if (mParameters.wake) {            policyFlags |= POLICY_FLAG_WAKE;        }    }    // Consume raw off-screen touches before cooking pointer data.    // If touches are consumed, subsequent code will not receive any pointer data.    if (consumeRawTouches(when, policyFlags)) {        mCurrentRawState.rawPointerData.clear();    }    // Cook pointer data.  This call populates the mCurrentCookedState.cookedPointerData structure    // with cooked pointer data that has the same ids and indices as the raw data.    // The following code can use either the raw or cooked data, as needed.    cookPointerData();    // Apply stylus pressure to current cooked state.    applyExternalStylusTouchState(when);    // Synthesize key down from raw buttons if needed.    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,            policyFlags, mLastCookedState.buttonState, mCurrentCookedState.buttonState);    // Dispatch the touches either directly or by translation through a pointer on screen.    if (mDeviceMode == DEVICE_MODE_POINTER) {        for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits);                !idBits.isEmpty(); ) {            uint32_t id = idBits.clearFirstMarkedBit();            const RawPointerData::Pointer& pointer =                    mCurrentRawState.rawPointerData.pointerForId(id);            if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS                    || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {                mCurrentCookedState.stylusIdBits.markBit(id);            } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER                    || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {                mCurrentCookedState.fingerIdBits.markBit(id);            } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) {                mCurrentCookedState.mouseIdBits.markBit(id);            }        }        for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits);                !idBits.isEmpty(); ) {            uint32_t id = idBits.clearFirstMarkedBit();            const RawPointerData::Pointer& pointer =                    mCurrentRawState.rawPointerData.pointerForId(id);            if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS                    || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {                mCurrentCookedState.stylusIdBits.markBit(id);            }        }        // Stylus takes precedence over all tools, then mouse, then finger.        PointerUsage pointerUsage = mPointerUsage;        if (!mCurrentCookedState.stylusIdBits.isEmpty()) {            mCurrentCookedState.mouseIdBits.clear();            mCurrentCookedState.fingerIdBits.clear();            pointerUsage = POINTER_USAGE_STYLUS;        } else if (!mCurrentCookedState.mouseIdBits.isEmpty()) {            mCurrentCookedState.fingerIdBits.clear();            pointerUsage = POINTER_USAGE_MOUSE;        } else if (!mCurrentCookedState.fingerIdBits.isEmpty() ||                isPointerDown(mCurrentRawState.buttonState)) {            pointerUsage = POINTER_USAGE_GESTURES;        }        dispatchPointerUsage(when, policyFlags, pointerUsage);    } else {        if (mDeviceMode == DEVICE_MODE_DIRECT                && mConfig.showTouches && mPointerController != NULL) {            mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);            mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);            mPointerController->setButtonState(mCurrentRawState.buttonState);            mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,                    mCurrentCookedState.cookedPointerData.idToIndex,                    mCurrentCookedState.cookedPointerData.touchingIdBits);        }        if (!mCurrentMotionAborted) {            dispatchButtonRelease(when, policyFlags);            dispatchHoverExit(when, policyFlags);            dispatchTouches(when, policyFlags);            dispatchHoverEnterAndMove(when, policyFlags);            dispatchButtonPress(when, policyFlags);        }        if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {            mCurrentMotionAborted = false;        }    }    // Synthesize key up from raw buttons if needed.    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,            policyFlags, mLastCookedState.buttonState, mCurrentCookedState.buttonState);    // Clear some transient state.    mCurrentRawState.rawVScroll = 0;    mCurrentRawState.rawHScroll = 0;    // Copy current touch to last touch in preparation for the next cycle.    mLastRawState.copyFrom(mCurrentRawState);    mLastCookedState.copyFrom(mCurrentCookedState);}
void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {      ....    dispatchMotion();      ....}
  • 在相关的函数调用之后,最终调用了dispatchTouches
  • 对于dispatchTouches中,会根据记录的上一次的触摸位置,对事件的类型进行判断,然后做相应的分发,事件类型有抬起,下落,移动等,然后对相应的事件进行分发。无论是对于何种类型的事件派发,最终被调用到的都是dispatchMotion()方法。
void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {      ....    dispatchMotion();      ....}
void TouchInputMapper::dispatchMotion() {   ....   NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,            action, actionButton, flags, metaState, buttonState, edgeFlags,            mViewport.displayId, pointerCount, pointerProperties, pointerCoords,            xPrecision, yPrecision, downTime);    getListener()->notifyMotion(&args);}

getListener函数

InputListenerInterface* InputReader::ContextImpl::getListener() {    return mReader->mQueuedListener.get();}

notifyMotion函数实现

void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {    mArgsQueue.push(new NotifyMotionArgs(*args));}

我们将触摸相关的事件进行包装之后,将其加入到一个ArgsQueue队列,到此,我们已经将数据加入到参数队列中

InputReader的loopOnce过程, 可知当执行完processEventsLocked()过程, 然后便开始执行mQueuedListener->flush()过程

2.3 QueuedInputListener的flush方法

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();}

遍历整个mArgsQueue数组, 在input架构中NotifyArgs的实现子类主要有以下几类:

  • NotifyConfigurationChangedArgs
  • NotifyKeyArgs
  • NotifyMotionArgs
  • NotifySwitchArgs
  • NotifyDeviceResetArgs

NotifyArgs的notify函数实现

void NotifyMotionArgs::notify(const sp& listener) const {    listener->notifyMotion(this);}

对于这个listener的创建来自于InputReader构建的时候。

mQueuedListener = new QueuedInputListener(listener);
 mReader = new InputReader(eventHub, readerPolicy, mDispatcher);

InputDispatcher
而这里的Listener则是InputDispatcherInputDispatchernotifyMotion实现源码。

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {    .....   MotionEvent event;   event.initialize(args->deviceId, args->source, args->action, args->actionButton,                    args->flags, args->edgeFlags, args->metaState, args->buttonState,                    0, 0, args->xPrecision, args->yPrecision,                    args->downTime, args->eventTime,                    args->pointerCount, args->pointerProperties, args->pointerCoords);    ....  //构造MotionEntry,然后将其加入到enqueueInboundEventLocked之中  MotionEntry* newEntry = new MotionEntry(args->eventTime,                args->deviceId, args->source, policyFlags,                args->action, args->actionButton, args->flags,                args->metaState, args->buttonState,                args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,                args->displayId,                args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0);   needWake = enqueueInboundEventLocked(newEntry);    ....   if (needWake) {      mLooper->wake();//唤醒其中的looper   }}

在该函数中,所做的事情是对于所传递的参数,构造MotionEntry,然后将其加入到enqueueInboundEventLocked之中。然后唤醒其中的looper

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {    bool needWake = mInboundQueue.isEmpty();    mInboundQueue.enqueueAtTail(entry);    ...    //进行一些事件和窗口相关的判断处理}

总结

Android触摸事件的传递(四-1)--输入系统-InputReader_第2张图片 触摸事件流程2.png

补充--设备增加

1. addDeviceLocked

void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);    if (deviceIndex >= 0) {        return; //已添加的相同设备则不再添加    }    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);    uint32_t classes = mEventHub->getDeviceClasses(deviceId);    int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);       InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);    device->configure(when, &mConfig, 0);    device->reset(when);    mDevices.add(deviceId, device); //添加设备到mDevices    ...}

2 createDeviceLocked

InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,        const InputDeviceIdentifier& identifier, uint32_t classes) {    //创建InputDevice对象    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),            controllerNumber, identifier, classes);    ...    //获取键盘源类型    uint32_t keyboardSource = 0;    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {        keyboardSource |= AINPUT_SOURCE_KEYBOARD;    }    if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;    }    if (classes & INPUT_DEVICE_CLASS_DPAD) {        keyboardSource |= AINPUT_SOURCE_DPAD;    }    if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {        keyboardSource |= AINPUT_SOURCE_GAMEPAD;    }    //添加键盘类设备InputMapper    if (keyboardSource != 0) {        device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));    }    //添加鼠标类设备InputMapper    if (classes & INPUT_DEVICE_CLASS_CURSOR) {        device->addMapper(new CursorInputMapper(device));    }    //添加触摸屏设备InputMapper    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {        device->addMapper(new MultiTouchInputMapper(device));    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {        device->addMapper(new SingleTouchInputMapper(device));    }    ...    return device;}

该方法主要功能:

  • 创建InputDevice对象,将InputReadermContext赋给InputDevice对象所对应的变量
  • 根据设备类型来创建并添加相对应的InputMapper,同时设置mContext.

input设备类型有很多种,以上代码只列举部分常见的设备以及相应的InputMapper:

  • 键盘类设备:KeyboardInputMapper
  • 触摸屏设备:MultiTouchInputMapper或SingleTouchInputMapper
  • 鼠标类设备:CursorInputMapper

参考

Android系统源码剖析-事件分发
Android 输入系统(三)InputReader

更多相关文章

  1. Android Chronometer控件实现计时器函数详解
  2. [置顶] 我的Android进阶之旅------>Ubuntu下不能识别Android设备
  3. 四、View的事件体系
  4. 【Android游戏开发十五】关于Android 游戏开发中 OnTouchEvent()
  5. Android学习07-----事件处理(3)监听日期与时间的改变_焦点事件_
  6. Android设备开机后自动启动APP解决方法:(学习篇)
  7. android Sipnner点击相同Item不响应OnItemSelected事件
  8. Android设备支持USB转RJ45有线网卡( 沐阳网卡JP1081B/9700)
  9. 谈谈 Android 中的各种设备标识符

随机推荐

  1. Android核心模块及相关技术
  2. Mac的android真机调试
  3. 第一讲:Android开发环境的搭建
  4. Android(安卓)更改软键盘Enter键为搜索
  5. Maven开发Android指南 1 简介
  6. android java代码的启动:app_process
  7. 布局与控件(三)-TextView那些事儿
  8. Android(安卓)SQLite 数据库存储
  9. -用Gradle 构建你的android程序
  10. 把TextView中的文字添加阴影效果及Style