Android的窗口机制分析-事件处理
[日期:2011-11-21] | 来源:Linux社区 作者:windskier |
2. 事件传递
经过分析事件处理系统的初始化过程之后,我们已经对事件处理系统的整体架构有了一定程度的理解,那么下面的事件传递过程就会显得很easy了。
2.1 InputReaderThread线程操作
当input系统有事件发生时,会被InputReaderThread线程轮询到,InputReader会根据事件的device id来选择的InputDevice,然后再根据事件的类型来选择InputDevice中的InputMapper,InputMapper会将事件信息通知给InputDispatcher;
目前adroid在InputReader中实现了5种设备类型的InputMapper,分别为滑盖/翻盖SwitchInputMapper、键盘KeyboardInputMapper、轨迹球TrackballInputMapper、多点触屏MultiTouchInputMapper以及单点触屏SingleTouchInputMapper。
设备类型 | InputManager | EventType | Notify InputDispatcher |
滑盖/翻盖 | SwitchInputMapper | EV_SW | notifySwitch() |
键盘 | KeyboardInputMapper | EV_KEY | notifyKey() |
轨迹球 | TrackballInputMapper | EV_KEY, EV_REL, EV_SYN | notifyMotion() |
单点触屏 | SingleTouchInputMapper | EV_KEY, EV_ABS, EV_SYN | notifyMotion() |
多点触屏 | MultiTouchInputMapper | EV_ABS, EV_SYN | notifyMotion() |
Notify InputDispatcher表示不同的事件通知InputDispatcher的函数调用,这几个函数虽然是被InputReaderThread调用的,单却是在InputDispatcher定义的。
2.1.1 notifySwitch()
- void InputDispatcher::notifySwitch(nsecs_t when, int32_t switchCode, int32_t switchValue,
- uint32_t policyFlags) {
- #if DEBUG_INBOUND_EVENT_DETAILS
- LOGD("notifySwitch - switchCode=%d, switchValue=%d, policyFlags=0x%x",
- switchCode, switchValue, policyFlags);
- #endif
- policyFlags |= POLICY_FLAG_TRUSTED;
- mPolicy->notifySwitch(when, switchCode, switchValue, policyFlags);
- }
- void NativeInputManager::notifySwitch(nsecs_t when, int32_t switchCode,
- int32_t switchValue, uint32_t policyFlags) {
- #if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("notifySwitch - when=%lld, switchCode=%d, switchValue=%d, policyFlags=0x%x",
- when, switchCode, switchValue, policyFlags);
- #endif
- JNIEnv* env = jniEnv();
- switch (switchCode) {
- case SW_LID:
- env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyLidSwitchChanged,
- when, switchValue == 0);
- checkAndClearExceptionFromCallback(env, "notifyLidSwitchChanged");
- break;
- }
- }
2.1.2 notifyKey()
- void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source,
- uint32_t policyFlags, int32_t action, int32_t flags,
- int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) {
- #if DEBUG_INBOUND_EVENT_DETAILS
- LOGD("notifyKey - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, action=0x%x, "
- "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld",
- eventTime, deviceId, source, policyFlags, action, flags,
- keyCode, scanCode, metaState, downTime);
- #endif
- if (! validateKeyEvent(action)) {
- return;
- }
- policyFlags |= POLICY_FLAG_TRUSTED;
- mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags,
- keyCode, scanCode, /*byref*/ policyFlags);
- bool needWake;
- { // acquire lock
- AutoMutex _l(mLock);
- int32_t repeatCount = 0;
- KeyEntry* newEntry = mAllocator.obtainKeyEntry(eventTime,
- deviceId, source, policyFlags, action, flags, keyCode, scanCode,
- metaState, repeatCount, downTime);
- needWake = enqueueInboundEventLocked(newEntry);
- } // release lock
- if (needWake) {
- mLooper->wake();
- }
- }
- Queue<EventEntry> mInboundQueue;
2.1.3 notifyMotion()
- mPolicy->interceptGenericBeforeQueueing(eventTime, /*byref*/ policyFlags);
2.2 InputDispatcherThread线程操作
InputDispatcherThread线程的轮询过程dispatchOnce()-->dispatchOnceInnerLocked(), InputDispatcherThread线程不停的执行该操作,以达到轮询的目的,我们的研究重点也就放在这2个函数处理上。
2.2.1 InputDispatcherThread基本流程
InputDispatcherThread的主要操作是分两块同时进行的,
一部分是对InputReader传递过来的事件进行dispatch前处理,比如确定focus window,特殊按键处理如HOME/ENDCALL等,在预处理完成 后,InputDispatcher会将事件存储到对应的focus window的outBoundQueue,这个outBoundQueue队列是InputDispatcher::Connection的成员函数,因此它是和ViewRoot相关的。
一部分是对looper的轮询,这个轮询过程是检查NativeInputQueue是否处理完成上一个事件,如果NativeInputQueue处理完成事件,它就会向通过管道向InputDispatcher发送消息指示consume完成,只有NativeInputQueue consume完成一个事件,InputDispatcher才会向共享内存写入另一个事件。
2.2.3 丢弃事件
并不是所有的InputReader发送来的事件我们都需要传递给应用,比如上节讲到的翻盖/滑盖事件,除此之外的按键,触屏,轨迹球(后两者统一按motion事件处理),也会有部分的事件被丢弃,InputDispatcher总会根据一些规则来丢弃掉一部分事件,我们来分析以下哪些情况下我们需要丢弃掉部分事件?
InputDispatcher.h中定义了一个包含有丢弃原因的枚举:
- enum DropReason {
- DROP_REASON_NOT_DROPPED = 0,
- DROP_REASON_POLICY = 1,
- DROP_REASON_APP_SWITCH = 2,
- DROP_REASON_DISABLED = 3,
- };
不需要丢弃
2. DROP_REASON_POLICY
设置为DROP_REASON_POLICY主要有两种情形:
A. 在InputReader notify InputDispatcher之前,Policy会判断不需要传递给应用的事件。如上一节所述。
B. 在InputDispatcher dispatch事件前,PhoneWindowManager使用方法interceptKeyBeforeDispatching()提前consume掉一些按键事件,如上面的流程图所示。
interceptKeyBeforeDispatching()主要对HOME/MENU/SEARCH按键的特殊处理,如果此时能被consume掉,那么在InputDispatcher 中将被丢弃。
3.DROP_REASON_APP_SWITCH
当有App switch 按键如HOME/ENDCALL按键发生时,当InputReader向InputDispatcher 传递app switch按键时,会设置一个APP_SWITCH_TIMEOUT 0.5S的超时时间,当0.5s超时时,InputDispatcher 尚未dispatch到这个app switch按键时,InputDispatcher 将会丢弃掉mInboundQueue中所有处在app switch按键前的按键事件。这么做的目的是保证app switch按键能够确保被处理。此时被丢弃掉的按键会被置为DROP_REASON_APP_SWITCH。
4. DROP_REASON_DISABLED
这个标志表示当前的InputDispatcher 被disable掉了,不能dispatch任何事件,比如当系统休眠时或者正在关机时会用到。