Android系统源码阅读(13):Input消息的分发过程
请对照AOSP版本:6.0.1_r50。学校电脑好渣,看源码时卡半天
先回顾一下前两篇文章。在设备没有事件输入的时候,InputReader和InputDispatcher都处于睡眠状态。当输入事件发生,InputReader首先被激活,然后发送读取消息,激活Dispatcher。Dispatcher被激活以后,将消息发送给当前激活窗口的主线程,然后睡眠等待主线程处理完这个事件。主线程被激活后,会处理相应的消息,处理完毕后反馈给Dispatcher,从而Dispatcher可以继续发送消息。
1. InputReader获取事件
回顾一下第11章4.2中,InputReader线程在获取事件以后,会调用processEventsLocked(mEventBuffer, count);
处理事件。
1.1
这里先根据event的种类进行分门别类的处理。
frameworks/native/services/inputflinger/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;
}
//处理这些事件
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.2
准备将事件交给设备进行处理。
frameworks/native/services/inputflinger/InputReader.cpp :
void InputReader::processEventsForDeviceLocked(int32_t deviceId,
const RawEvent* rawEvents, size_t count) {
//判断设备是否是已知的
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
if (deviceIndex < 0) {
ALOGW("Discarding event for unknown deviceId %d.", deviceId);
return;
}
//获得设备
InputDevice* device = mDevices.valueAt(deviceIndex);
if (device->isIgnored()) {
//ALOGD("Discarding event for ignored deviceId %d.", deviceId);
return;
}
//交给设备进行处理
device->process(rawEvents, count);
}
1.3
用device中的mapper去映射传入的事件,然后再处理。
frameworks/native/services/inputflinger/InputReader.cpp :
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
// Process all of the events in order for each mapper.
// We cannot simply ask each mapper to process them in bulk because mappers may
// have side-effects that must be interleaved. For example, joystick movement events and
// gamepad button presses are handled by different mappers but they should be dispatched
// in the order received.
//一个设备可能有多种类型的event,所有有多个mapper,但是需要保持event的顺序性
//所以这里采用先循环event,再循环mapper的方式
size_t numMappers = mMappers.size();
for (const RawEvent* rawEvent = rawEvents; count--; 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) {
//..
mDropUntilNextSync = true;
reset(rawEvent->when);
} else {
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
//用每一种mapper尝试处理event
mapper->process(rawEvent);
}
}
}
}
1.4
这里InputMapper种类很多,每个事件处理方法各不相同,所以在这里不再详述。其中有如下的InputMapper:
SwitchInputMapper, VibratorInputMapper, KeyboardInputMapper, CursorInputMapper, TouchInputMapper, SingleTouchInputMapper, MultiTouchInputMapper, JoystickInputMapper
这些方法处理到最后,会调用getListener()->notifyXXX(&args)
,让Dispatcher进行分发,XXX根据不同的Mapper有相应的名字。在创建InputReader时,将InputDispatcher作为参数传入,同时建立了QueuedInputListener来存放这个InputDispatcher,估计是准备将来处理多个InputDispatcher。所以这里getListener获取的就是当初建立的InputDispatcher对象。
1.5
这里虽然已经开始调用InputDispatcher的函数,但是还是在InputReader线程中。这里开始向InputDispatcher的队列中插入事件,并且把InputDispatcher唤醒了。因为notifyXXX函数同样是针对不同的输入有着不同的处理,所以不再详述,截取一段MotionEvent的代码片段。
frameworks/native/services/inputflinger/InputDispatcher.cpp :
//针对每种event,都会想将其封装成一个EventEntry
// Just enqueue a new motion event.
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);
//...
//唤醒Looper线程
if (needWake) {
mLooper->wake();
}
将一个事件放入队列之后,会根据needWake参数决定是否要唤醒线程。如果要唤醒,则调用Looper的wake函数就可以了,和原来道理一样。在有些时候,有事件添加进去,不一定要唤醒线程,比如线程正在等待应用反馈事件处理完毕的消息。
1.6
实实在在的将这个EventEntry放入队列mInboundQueue中了。
frameworks/native/services/inputflinger/InputDispatcher.cpp :
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
//如果队列空了,需要唤醒
bool needWake = mInboundQueue.isEmpty();
//将事件加入队列
mInboundQueue.enqueueAtTail(entry);
traceInboundQueueLengthLocked();
switch (entry->type) {
//这里会优化App切换的事件,如果上一个App还有事件没处理完,也没反馈事件处理完毕消息
//则清空之前的事件,切换下一个应用
case EventEntry::TYPE_KEY: {
// Optimize app switch latency.
// If the application takes too long to catch up then we drop all events preceding
// the app switch key.
KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
if (isAppSwitchKeyEventLocked(keyEntry)) {
if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
mAppSwitchSawKeyDown = true;
} else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
if (mAppSwitchSawKeyDown) {
#if DEBUG_APP_SWITCH
ALOGD("App switch is pending!");
#endif
mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
mAppSwitchSawKeyDown = false;
needWake = true;
}
}
}
break;
}
//当一个非当前激活app的点击事件发生,会清空之前的事件
//从这个新的点击事件开始
case EventEntry::TYPE_MOTION: {
// Optimize case where the current application is unresponsive and the user
// decides to touch a window in a different application.
// If the application takes too long to catch up then we drop all events preceding
// the touch into the other window.
MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN
&& (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
&& mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
&& mInputTargetWaitApplicationHandle != NULL) {
int32_t displayId = motionEntry->displayId;
int32_t x = int32_t(motionEntry->pointerCoords[0].
getAxisValue(AMOTION_EVENT_AXIS_X));
int32_t y = int32_t(motionEntry->pointerCoords[0].
getAxisValue(AMOTION_EVENT_AXIS_Y));
sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);
if (touchedWindowHandle != NULL
&& touchedWindowHandle->inputApplicationHandle
!= mInputTargetWaitApplicationHandle) {
// User touched a different application than the one we are waiting on.
// Flag the event, and start pruning the input queue.
mNextUnblockedEvent = motionEntry;
needWake = true;
}
}
break;
}
}
return needWake;
}
这里做了两种优化,主要是在当前App窗口处理事件过慢,同时你又触发其他App的事件时,Dispatcher就会丢弃先前的事件,从这个开始唤醒Dispatcher。这样做很合情合理,用户在使用时,会遇到App由于开发者水平有限导致处理事件过慢情况,这时用户等的不耐烦,则应该让用户轻松的切换到其它App,而不是阻塞在那。所以,事件无法响应只会发生在App内部,而不会影响应用的切换,从而提升用户体验。App的质量问题不会影响系统的运转,Android在这点上做的很人性。
2. InputDispatcher分发事件
在第11章中3.2中,Dispatcher调用函数dispatchOnceInnerLocked(&nextWakeupTime);
来分配队列中的事件。
2.1
从队列中获取event,然后准备处理。
frameworks/native/services/inputflinger/InputDispatcher.cpp :
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
nsecs_t currentTime = now();
// Reset the key repeat timer whenever normal dispatch is suspended while the
// device is in a non-interactive state. This is to ensure that we abort a key
// repeat if the device is just coming out of sleep.
if