Android触摸事件的传递(四-1)--输入系统-InputReader
了解更多,移步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
则会 退出循环。 - 整个过程是不断循环的地调用
InputReader
的loopOnce()
方法。
2 loopOnce();
- 查看InputReader配置是否修改,如界面大小、方向、键盘布局重新加载、指针速度改变等
- 从EventHub读取事件,其中EVENT_BUFFER_SIZE = 256
- 处理事件
- 将事件传到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); } ....}
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
则是InputDispatcher
,InputDispatcher
的notifyMotion
实现源码。
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); ... //进行一些事件和窗口相关的判断处理}
总结
触摸事件流程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
对象,将InputReader
的mContext
赋给InputDevice
对象所对应的变量 - 根据设备类型来创建并添加相对应的
InputMapper
,同时设置mContext.
input设备类型有很多种,以上代码只列举部分常见的设备以及相应的InputMapper:
- 键盘类设备:KeyboardInputMapper
- 触摸屏设备:MultiTouchInputMapper或SingleTouchInputMapper
- 鼠标类设备:CursorInputMapper
参考
Android系统源码剖析-事件分发
Android 输入系统(三)InputReader
更多相关文章
- Android Chronometer控件实现计时器函数详解
- [置顶] 我的Android进阶之旅------>Ubuntu下不能识别Android设备
- 四、View的事件体系
- 【Android游戏开发十五】关于Android 游戏开发中 OnTouchEvent()
- Android学习07-----事件处理(3)监听日期与时间的改变_焦点事件_
- Android设备开机后自动启动APP解决方法:(学习篇)
- android Sipnner点击相同Item不响应OnItemSelected事件
- Android设备支持USB转RJ45有线网卡( 沐阳网卡JP1081B/9700)
- 谈谈 Android 中的各种设备标识符