前序
一般情况下很多同学对于点击事件的认识都只存在于从 Activity 开始的,然后从 Window 中进行分发,或者有些人有过自定义 View 的经验可能会涉及事件拦截处理等,会用到 onTouchEvent 和 dispatchTouchEvetn 这几个方法,会处理屏幕滑动,Down 事件,Up 事件等,但仅停留于对于 View 层的了解。
自定义 View 的事件处理其实在整个 Android 输入系统中只能算是最上层的,但这些事件是怎么产生的,上层 View 是怎么获得到这些事件的呢?喜欢研究底层实现原理的我,今天就给大家讲讲。
输入系统
事件产生的底层主要是输入子系统,Android 中的输入设备有很多,例如屏幕,鼠标,键盘等都是输入设备,对于应用开发者,接触最多的也就是屏幕了。当输入设备可用时,Linux 会在 /dev/input 中创建对应的设备节点。用户操作输入设备就会产生各种事件,这些事件的原始信息就会被 Linux 内核中的输入子系统采集,原始信息由 Kernel space 的驱动层一直传递到设备结点。
Android 提供了一些 api 可以让开发者在设备节点(dev/input/)中读取内核写入的事件。
InputManagerService
IMS 的工作就是监听 /dev/input 下所有设备的节点,当有数据时就会进行加工处理,并交给中间层的 WMS 派发给合适的 Window。后面简称为 IMS。
WindowManagerService
Android 的 WMS 主要是负责窗口管理,窗口的启动、添加、删除,管理窗口大小、层级等,WMS 与 IMS 息息相关。IMS 将底层封装好的事件通过 Socket pair 传递给 WMS,WMS 通过绑定好的 InputChannel 找到对应的窗口进行 app 层的事件分发,这时候就到我们熟悉的 Activity 的事件分发流程了。
下面借张图,整体流程画的非常详细:
今天我们主要讲底层硬件驱动产生的事件的获取及分发流程,至于硬件驱动层就不展开介绍了。而这里面最重要的就是 IMS 相关的流程,下面开始补足知识盲区。
IMS 的初始化
IMS 的构造
和 WMS 一样,IMS 也是在 SystemServer 中创建的 ,也是 startOtherServers 方法中。
private void startOtherServices(@NonNull TimingsTraceAndSlog t) { ... inputManager = new InputManagerService(context); wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore, new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager); ... inputManager.start(); }
复制代码
主要做了三件事:
调用了 IMS 的构造方法
将 IMS 作为参数传递给 WMS
调用了 IMS 的启动方法 start
IMS 构造函数
public InputManagerService(Context context) { this.mContext = context; //注释1 this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper()); mStaticAssociations = loadStaticInputPortAssociations(); mUseDevInputEventForAudioJack = context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack=" + mUseDevInputEventForAudioJack); //注释2 mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue()); }
复制代码
我们重点关注 1 创建了一个在 android.display 线程的 InputManagerHandler,所以后面这个 handler 处理的消息都是执行在 android.display 线程中,android.display 线程是系统共享的单例前台线程,可以用做一些低延时显示的相关操作,WMS 的创建也是在 android.display 中创建的。
注释 2 的话,调用了 nativeInit 方法,进入 native 侧创建了 NativeInputManager ,并将该对象指针返回给了 java 层,方便后续调用处理。
static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject serviceObj, jobject contextObj, jobject messageQueueObj) { sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); if (messageQueue == nullptr) { jniThrowRuntimeException(env, "MessageQueue is not initialized."); return 0; } NativeInputManager* im = new NativeInputManager(contextObj, serviceObj, messageQueue->getLooper()); im->incStrong(0); return reinterpret_cast<jlong>(im);}
复制代码
接下来看下 NativeInputManager 的创建
NativeInputManager::NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper) : mLooper(looper), mInteractive(true) { JNIEnv* env = jniEnv(); mContextObj = env->NewGlobalRef(contextObj);//注释1 mServiceObj = env->NewGlobalRef(serviceObj);//注释2 ... sp<EventHub> eventHub = new EventHub();//注释3 mInputManager = new InputManager(eventHub, this, this);//4 }
复制代码
注释 1:将 java 层的 Context 上下文保存在 mContextObj 中
注释 2:将 java 层传递下来的 InputManagerService 对象保存在 mServiceObj 中。
注释 3:创建了一个 EventHub,EventHub 通过 Linux 内核的 Notify 与 Epoll 机制监听设备节点,通过 EventHub 的 getEvent 函数读取设备节点的增删事件和原始输入事件。
最后创建了 InputManager 对象
InputManager::InputManager( const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { //注释1 mDispatcher = createInputDispatcher(dispatcherPolicy); mClassifier = new InputClassifier(mDispatcher); //注释2 mReader = createInputReader(readerPolicy, mClassifier); } sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy, const sp<InputListenerInterface>& listener) { return new InputReader(std::make_unique<EventHub>(), policy, listener); }
复制代码
其内部会创建 InputDispatcher 和 InputReader。
InputDispatcher,该类主要用于对原始事件的分发,传递给 WMS。
InputReader,会不断的读取 EventHub 中的原始信息进行加工并交给 InputDispatcher ,InputDispatcher 中保存了 Window 的信息(WMS 会将窗口的信息实时更新到 InputDispatcher 中),可以将事件信息派发到合适的窗口,InputReader 和 InputDispatcher 都是耗时操作,会在单独线程中执行。
IMS 构造主要是创建了 NativeInputManager 的 native 对象并返回给 java 层,将 java 层的 IMS 对象保存在 native 层的 mServiceObj 中,创建用于读取设备节点事件的 EventHub 对象。在 InputManager 构造中,创建了一个 InputDispatcher 和 InputReader 对象,以及用于读取事件的 InputReaderThread 线程和分发事件的 InputDispatcherThread 线程。
IMS 启动
在 IMS 创建完成之后,就会调用他的 start 方法进行启动
public void start() { nativeStart(mPtr);}
复制代码
调用 native 的 start 并传入 native 层 NativeInputManager 对象的指针,会调 native 的 InputManager 的 start 方法,该方法内部分别会 IputDispatcher 和 InputReader 的 start 方法。
status_t InputManager::start() { //注释1 status_t result = mDispatcher->start(); if (result) { ALOGE("Could not start InputDispatcher thread due to error %d.", result); return result; } //注释2 result = mReader->start(); if (result) { ALOGE("Could not start InputReader due to error %d.", result); mDispatcher->stop(); return result; } return OK; }
复制代码
InputReader 读取事件
status_t InputReader::start() { if (mThread) { return ALREADY_EXISTS; } mThread = std::make_unique<InputThread>( "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); }); return OK; }
复制代码
InputReader 中实例化了一个 InputThread ,该类继承了 Thread,其内部有一个循环,当线程运行时,会调用 threadloop 函数,如果返回了 true 并且没有调用 requestExit 函数,就会循环调用 threadloop 函数,线程中真正执行的是 loopOnce
void InputReader::loopOnce() { ...... //1 size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); { // acquire lock if (count) { //2 processEventsLocked(mEventBuffer, count); } } // release lock ...... //3 mQueuedListener->flush(); }
复制代码
主要做三件事:
调用 EventHub 的 getEvents 函数来获取设备节点的信息到 mEventBuffer 中,事件信息主要有两种,一种是设备的增删事件(设备事件),一种是原始的输入事件,注:方法内部在没有输入事件的时候会进行休眠,并不会持续占用 CPU
对 mEventBuffer 中的输入事件信息进行加工处理,加工处理后的事件会交给 InputDispatcher 来处理
如果无事件就调用 flush 方法,一次性将所有事件给 InputDispatcher 处理
我们继续看下怎么对事件进行加工处理的,
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { for (const RawEvent* rawEvent = rawEvents; count;) { int32_t type = rawEvent->type; size_t batchSize = 1; //注释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; } //注释2 processEventsForDeviceLocked(deviceId, rawEvent, batchSize); } else { switch (rawEvent->type) {//3 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; } } void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) { ... std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier); ... mDevices.emplace(eventHubId, device); ... }
复制代码
所有事件用 RawEvent 封装,但对原始原始事件和设备事件分开处理事件和设备事件分开处理,其中设备事件有三种类型,分别是节点的添加,删除、扫描等操作,原始事件主要是交给 processEventsForDeviceLocked 处理,先来看下 processEventsForDeviceLocked 逻辑。
void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents, size_t count) { //注释1 auto deviceIt = mDevices.find(eventHubId); if (deviceIt == mDevices.end()) { ALOGW("Discarding event for unknown eventHubId %d.", eventHubId); return; } //注释2 std::shared_ptr<InputDevice>& device = deviceIt->second; if (device->isIgnored()) { // ALOGD("Discarding event for ignored deviceId %d.", deviceId); return; } device->process(rawEvents, count); }
复制代码
mDevices 中存的 key 是 eventHub,而 value 就是设备 InputDevice,查到后进行处理
void InputDevice::process(const RawEvent* rawEvents, size_t count) { for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) { if (mDropUntilNextSync) { if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { mDropUntilNextSync = false; } else { ..... } } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) { //缓冲区溢出 ALOGI("Detected input event buffer overrun for device %s.", getName().c_str()); mDropUntilNextSync = true; reset(rawEvent->when); } else { for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) { mapper.process(rawEvent); }); } --count; } } inline void for_each_mapper_in_subdevice(int32_t eventHubDevice, std::function<void(InputMapper&)> f) { auto deviceIt = mDevices.find(eventHubDevice); if (deviceIt != mDevices.end()) { auto& devicePair = deviceIt->second; auto& mappers = devicePair.second; for (auto& mapperPtr : mappers) { f(*mapperPtr); } }
复制代码
最终是使用不同的 InputMapper 进行处理,原始输入事件的类型很多,因此 InputMapper 有很多子类用于加工不同的原始输入事件,例如 TouchInputMapper 用于处理触摸输入事件,KeyboardInputMapper 处理键盘输入事件等。以触摸事件为例,看下 TouchInputMapper 执行。
void TouchInputMapper::process(const RawEvent* rawEvent) { mCursorButtonAccumulator.process(rawEvent); //处理鼠标事件,type == EV_KEY 进行处理 mCursorScrollAccumulator.process(rawEvent); //处理鼠标滚轮事件,type == EV_REL 进行处理 mTouchButtonAccumulator.process(rawEvent); //处理屏幕触摸事件,type == EV_KEY 进行处理 if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { sync(rawEvent->when, rawEvent->readTime); } }
复制代码
void TouchInputMapper::processRawTouches(bool timeout) { .... cookAndDispatch(when, readTime); .... }
复制代码
void TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) { BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits; BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits; int32_t metaState = getContext()->getGlobalMetaState(); int32_t buttonState = mCurrentCookedState.buttonState; // Dispatch pointer up events. while (!upIdBits.isEmpty()) { ..... dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState, buttonState, 0, mLastCookedState.cookedPointerData.pointerProperties, mLastCookedState.cookedPointerData.pointerCoords, mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId, mOrientedXPrecision, mOrientedYPrecision, mDownTime); dispatchedIdBits.clearBit(upId); mCurrentCookedState.cookedPointerData.canceledIdBits.clearBit(upId); } // Dispatch move events if any of the remaining pointers moved from their old locations. // Although applications receive new locations as part of individual pointer up // events, they do not generally handle them except when presented in a move event. if (moveNeeded && !moveIdBits.isEmpty()) { ... dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, 0, mCurrentCookedState.cookedPointerData.pointerProperties, mCurrentCookedState.cookedPointerData.pointerCoords, mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime); } // Dispatch pointer down events using the new pointer locations. while (!downIdBits.isEmpty()) { ... dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState, 0, mCurrentCookedState.cookedPointerData.pointerProperties, mCurrentCookedState.cookedPointerData.pointerCoords, mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime); } }
复制代码
会根据记录的上一次触摸位置,根据事件类型做相应的分发。事件类型有 Up,Down,Move 等,最终调用
void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source, int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags, const PointerProperties* properties, const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime) { //最终形成的 NoteifyMotionArgs 对象 NotifyMotionArgs args(getContext()->getNextId(), when, readTime, deviceId, source, displayId, policyFlags, action, actionButton, flags, metaState, buttonState, MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties, pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,downTime, std::move(frames)); //回调到 InputDispatcher 的 notifyMotion 方法中 getListener()->notifyMotion(&args); }
复制代码
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { ....... bool needWake; { // Just enqueue a new motion event. std::unique_ptr<MotionEntry> newEntry = std::make_unique<MotionEntry>(args->id, args->eventTime, args->deviceId, ......); needWake = enqueueInboundEventLocked(std::move(newEntry)); mLock.unlock(); } // release lock if (needWake) { mLooper->wake(); } } bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newEntry) { bool needWake = mInboundQueue.empty(); //将事件添加到 minboundQueue 中 mInboundQueue.push_back(std::move(newEntry)); EventEntry& entry = *(mInboundQueue.back()); traceInboundQueueLengthLocked(); ... return needWake; }
复制代码
最终调用 enqueueInboundEventLocked 方法并将 MotionEntry 添加到队列,然后决定是否唤醒 InputDispatcher 线程。
获取事件的流程至此结束,我们总结下:
InputReader 只是调用 EventHub 的 getEvent 获取了原始事件,获取到事件后,就会根据原始事件找到对应的 InputDevice(设备对象)。
在 InputDevice 中,根据事件获取到对应的 InputMapper 用于加工事件。InputMapper 子类分别对应了不同的事件类型,例如触摸事件,多点触摸事件,按键事件等。
最终事件被加工结束后都会通过 getListener 回调掉 InputDispatcher 中对应的两个方法,比如触摸事件被封装成了 MotionEntry 对象。最后调用 enqueueInboundEventLocked 方法,决定是否需要唤醒 InputDispatcher 线程
总结
由于篇幅过长,为避免带来阅读不适,事件分发后面再讲。感谢关注,感谢一键三连。
微信公众号首发,第一时间获取新知识请关注,感谢支持,欢迎大家关注、转发、评论。
评论