Android InputDispatch事件派发->选择目标窗口
WindowManagerService的主要两大作用:
1 和surfaceflinger交互,创建surface, 通知surfacelinger窗口的层级、大小、位置等属性。
2 和inputflinger交互, 告知inputflinger当前窗口大小位置,是否可以接受input事件以及窗口可以处理什么类型的事件。
surfacelinger和inputflinger都是系统里面两个负载较重的服务。为了分析WindowManagerService我准备先从inputflinger这端着手,因为这端相对比较容易,采用自顶向下的方式来进行分析。所以今天先看下inputflinger如何将根据WMS告知的窗口情况来派发input事件。
dumpsys是比较好的工具,我们此次不考虑WMS如何告知inputflinger窗口情况,只关心inputflinger如何根据窗口情况派发输入事件。下面是我在桌面启动了一个全屏应用的()场景,使用adb shell dumpsys input 输出如下:
Input Dispatcher State:DispatchEnabled: 1DispatchFrozen: 0FocusedApplication: name='AppWindowToken{9ab95fe token=Token{867fdb9 ActivityRecord{c189380 u0 com.android.contacts/.activities.CompactContactEditorActivity t6}}}', dispatchingTimeout=5000.000msFocusedWindow: name='Window{c7ac47b u0 com.android.contacts/com.android.contacts.activities.CompactContactEditorActivity}'TouchStates: <no displays touched>Windows:0: name='Window{6887dc9 u0 NavigationBar}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=true, canReceiveKeys=false, flags=0x01840068, type=0x000007e3, layer=211000, frame=[0,432][320,480], scale=1.000000, touchableRegion=[0,432][320,480], inputFeatures=0x00000000, ownerPid=1408, ownerUid=10016, dispatchingTimeout=5000.000ms1: name='Window{7d5502a u0 StatusBar}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=true, canReceiveKeys=false, flags=0x81840048, type=0x000007d0, layer=161000, frame=[0,0][320,24], scale=1.000000, touchableRegion=[0,0][320,24], inputFeatures=0x00000000, ownerPid=1408, ownerUid=10016, dispatchingTimeout=5000.000ms2: name='Window{25377a1 u0 KeyguardScrim}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=false, canReceiveKeys=false, flags=0x01110900, type=0x000007ed, layer=141000, frame=[0,0][320,432], scale=1.000000, touchableRegion=[0,0][320,432], inputFeatures=0x00000000, ownerPid=1303, ownerUid=1000, dispatchingTimeout=5000.000ms3: name='Window{3dc7d4 u0 AssistPreviewPanel}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=false, canReceiveKeys=false, flags=0x01000118, type=0x000007f1, layer=41000, frame=[0,432][320,432], scale=1.000000, touchableRegion=<empty>, inputFeatures=0x00000000, ownerPid=1408, ownerUid=10016, dispatchingTimeout=5000.000ms4: name='Window{c7ac47b u0 com.android.contacts/com.android.contacts.activities.CompactContactEditorActivity}', displayId=0, paused=false, hasFocus=true, hasWallpaper=false, visible=true, canReceiveKeys=true, flags=0x81810120, type=0x00000001, layer=21015, frame=[0,0][320,480], scale=1.000000, touchableRegion=[0,0][320,480], inputFeatures=0x00000000, ownerPid=1993, ownerUid=10002, dispatchingTimeout=5000.000ms5: name='Window{a245c67 u0 com.android.contacts/com.android.contacts.activities.PeopleActivity}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=false, canReceiveKeys=false, flags=0x81810120, type=0x00000001, layer=21010, frame=[0,0][320,480], scale=1.000000, touchableRegion=[0,0][320,480], inputFeatures=0x00000000, ownerPid=1993, ownerUid=10002, dispatchingTimeout=5000.000ms6: name='Window{e911fed u0 com.android.launcher/com.android.launcher2.Launcher}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=false, canReceiveKeys=false, flags=0x01910120, type=0x00000001, layer=21005, frame=[0,0][320,432], scale=1.000000, touchableRegion=[0,0][320,480], inputFeatures=0x00000000, ownerPid=1601, ownerUid=10008, dispatchingTimeout=5000.000ms7: name='Window{5df78f9 u0 com.android.systemui.ImageWallpaper}', displayId=0, paused=false, hasFocus=false, hasWallpaper=false, visible=true, canReceiveKeys=false, flags=0x00000318, type=0x000007dd, layer=21000, frame=[0,0][960,800], scale=1.000000, touchableRegion=[0,0][960,800], inputFeatures=0x00000000, ownerPid=1408, ownerUid=10016, dispatchingTimeout=5000.000ms
输出中windows的信息都来自InputDispatcher的成员变量mWindowHandles,数据结构第一如下
Vector<sp<InputWindowHandle> > mWindowHandles;
mWindowHandles为数组结构,每个元素为一个InputWindowHandle结构,代表一个窗口, 这些InputWindowHandle是Wms告诉inputflinger的,按照z-order从高到低的顺序存储在mWindowHandles数组中。每个InputWindowHandle的成员变量mInfo 描述了window的一些情况。mInfo的类型为InputWindowInfo:
struct InputWindowInfo {sp<InputChannel> inputChannel; // 事件派发通道String8 name; // window 名称int32_t layoutParamsFlags; // flagsint32_t layoutParamsType; // 类型nsecs_t dispatchingTimeout; // anr超时时间int32_t frameLeft; // 坐标int32_t frameTop;int32_t frameRight;int32_t frameBottom; float scaleFactor; // 缩放Region touchableRegion; // 可触摸区域bool visible; // 是否可见bool canReceiveKeys; // 是否可以接收key时间bool hasFocus; // 是否是焦点window (默认key事件发给焦点window)bool hasWallpaper; // 是否能显示壁纸bool paused; // 是否处于暂停状态int32_t layer; // layer 也就是层级z-orderint32_t ownerPid; // 所属应用进程的pidint32_t ownerUid; // 所属应用进程的uidint32_t inputFeatures; // 支持的featureint32_t displayId; // 所属的屏幕(display) id
}
layoutParamsFlags变量描述了window能处理哪些事件。frameLeft、frameTop、frameRight、frameBottom、scaleFactor、touchableRegion描述了window的坐标,一般情况下window只能处理落在自身范围内的输入事件。visible则表示window是否可见,不可见的window也不能处理input事件。hasFocus变量则表示window是否是焦点window,一般key事件都发送到焦点window。displayId则用于标识window所属的屏幕(display),window只处理落在自己屏幕上的input事件。有了这些背景只是我们来分析代码实现。
对于一个MotionEvent 可以能处理该事件的Window并不是只有焦点窗口,因为Window有一些特殊的layoutParamsFlags可以,可以用于接收到触摸到window外部的事件,我们比较熟悉的PopupWindow.setOutsideTouchable(boolean touchable)就会设置一个比较特殊的layoutParamsFlags(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH)。设置该参数的Flags就会收到点击到Window外部的事件。 另外Dialog也有一个函数Dialog.setCanceledOnTouchOutside(boolean cancel) 函数如果参数cancel为true,那么input系统就会把不在Dialog window范围内的事件发送给它,这是通过window的layoutParamsFlags另外一个表示WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL实现的。
下面我列出几个在window事件处理中比较常见的flag。这些flag都是WindowManager.LayoutParams下的常亮。
FLAG | 描述 |
---|---|
FLAG_NOT_FOCUSABLE | 该标志表示window不会获取焦点,不接收key事件 |
FLAG_NOT_TOUCHABLE | 该标志表示window不会处理touch事件 |
FLAG_NOT_TOUCH_MODAL | 该标志表示该window允许将超出该窗口的事件发送给后面的窗口处理,否则事件不会发送到后面窗口,该窗口会处理所有事件,当设置了FLAG_NOT_FOCUSABLE标志,该标志自动置1 |
FLAG_WATCH_OUTSIDE_TOUCH | 当点击了window外部区域,该window可以收到一个ACTION_OUTSIDE事件 |
对于真正处理事件的window,对于相同的MotionEvent,假设是一个ACTION_DOWN事件,目标window要收到一个ACTION_DOWN事件,但是其他window可能要收到ACTION_OUTSIDE事件,这样同一个MotionEvent对多个window产生了不同的事件,这是通过给该window一个TargetFlags来实现的,有些TargetFlag是一次性的。关于TargetFlag描述如下:
FLAG | 描述 |
---|---|
FLAG_DISPATCH_AS_IS | 表示序列事件应该一直发送给该window,没有该标志只需要发送一个事件给该window |
FLAG_DISPATCH_AS_OUTSIDE | 发送一个ACTION_OUTSIDE给该window |
FLAG_DISPATCH_AS_HOVER_EXIT | 发送一个ACTION_HOVER_EXIT事件给该window |
FLAG_DISPATCH_AS_HOVER_ENTER | 发送一个ACTION_HOVER_ENTER给该window |
FLAG_DISPATCH_AS_SLIPPERY_EXIT | 发送一个ACTION_CANCEL给该window |
FLAG_DISPATCH_AS_SLIPPERY_ENTER | 发送一个ACTION_DOWN给该window |
除了上述知识点外,我们还有一点需要理解,一个Motion事件序列一般是: ACTION_DOWN->一系列ACTION_MOVE->ACTION_UP|ACTION_CANCEL。一般在收到ACTION_DOWN的时候确定目标window,后续ACTION_MOVE和ACTION_UP|ACTION_CANCEL事件都应该发送给该窗口,即使我们的ACTION_MOVE已经超出了该window的范围也应该将该事件派发给该window,所以一般确定派发window的在序列事件开始的地方。
下面我们来通过代码来详细解读Input系统如何选择目标窗口,代码是InputDispatcher的findTouchdWindowTargetsLocked
frameworks/native/services/inputflinger/InputDispatcher.cpp
1129 int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
1130 const MotionEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
1131 bool* outConflictingPointerActions) {1132 enum InjectionPermission {1133 INJECTION_PERMISSION_UNKNOWN,
1134 INJECTION_PERMISSION_GRANTED,
1135 INJECTION_PERMISSION_DENIED
1136 };
1137
1138 nsecs_t startTime = now();
1139
1140 // For security reasons, we defer updating the touch state until we are sure that
1141 // event injection will be allowed.
1142 int32_t displayId = entry->displayId;
1143 int32_t action = entry->action;
1144 int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
1145
1146 // Update the touch state as needed based on the properties of the touch event.
1147 int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING;
1148 InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
1149 sp<InputWindowHandle> newHoverWindowHandle;
1150
1151 // Copy current touch state into mTempTouchState.
1152 // This state is always reset at the end of this function, so if we don't find state
1153 // for the specified display then our initial state will be empty.
1154 const TouchState* oldState = NULL;
1155 ssize_t oldStateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
1156 if (oldStateIndex >= 0) {1157 oldState = &mTouchStatesByDisplay.valueAt(oldStateIndex);// 复制屏幕处理input事件的状态到mTempTouchState中
1158 mTempTouchState.copyFrom(*oldState);
1159 }
1160
1161 bool isSplit = mTempTouchState.split;
1162 bool switchedDevice = mTempTouchState.deviceId >= 0 && mTempTouchState.displayId >= 0
1163 && (mTempTouchState.deviceId != entry->deviceId
1164 || mTempTouchState.source != entry->source
1165 || mTempTouchState.displayId != displayId);
1166 bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
1167 || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER
1168 || maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
1169 bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN
1170 || maskedAction == AMOTION_EVENT_ACTION_SCROLL
1171 || isHoverAction);
1172 bool wrongDevice = false;
1173 if (newGesture) {// 对于新的操作序列开始,清空旧的事件处理状态mTempTouchState。
1174 bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
1175 if (switchedDevice && mTempTouchState.down && !down) {1176 #if DEBUG_FOCUS
1177 ALOGW("Dropping event because a pointer for a different device is already down.");
1178 #endif
1179 injectionResult = INPUT_EVENT_INJECTION_FAILED;
1180 switchedDevice = false;
1181 wrongDevice = true;
1182 goto Failed;
1183 }
1184 mTempTouchState.reset();
1185 mTempTouchState.down = down;
1186 mTempTouchState.deviceId = entry->deviceId;
1187 mTempTouchState.source = entry->source;
1188 mTempTouchState.displayId = displayId;
1189 isSplit = false;
1190 }
1191
1192 if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {1193 /* Case 1: New splittable pointer going down, or need target for hover or scroll. */// case 1 处理新的操作序列开始,对于当前操作状态为isSplit的状态的情况,AMOTION_EVENT_ACTION_POINTER_DOWN// 函数将被当新操作序列开始处理......
1285 } else {1286 /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */// case 2: mov,up,或者cancel或者pointer down事件,应该发送给之前处理case1的对应window上。......
1341 }
findTouchedWindowTargetsLocked函数比较长我们分段去看,函数的参数currentTime表示当前时间,entry表示要派发的input事件,这里类型为MotionEntry表示为触摸事件。inputTargets是一个输出变量,用于保存最终要处理该事件(entry)对应的window集合。nextWakeupTime也是一个输出变量,用于通知InputDispatcher没有事件处理的时候休眠多久。outConflictingPointerActions也是一个输出变量,用于反馈上级函数当前事件是否有冲突。
对于input事件的处理Android要维护一个状态,要根据当前状态去处理下一个事件。比如我们的一次滑动事件,起始位置落在了普通应用的窗口上,但是我们一直向上滑动,一直滑动到statusbar所在的位置,那么这个事件应该继续派发给普通应用程序而不应该派发给statusbar,因为我们需要事件的连续性,即使statusbar在普通应用窗口上面。 为了实现这个功能就需要做动作序列的追踪。Android的InputDispatcher为每个屏幕创建一个TouchState变量来追踪落在该屏幕的序列事件状态。
struct TouchState {bool down;bool split;int32_t deviceId; // id of the device that is currently down, others are rejecteduint32_t source; // source of the device that is current down, others are rejectedint32_t displayId; // id to the display that currently has a touch, others are rejected}
down 表示该屏幕已经开始处理一个事件序列(一个input序列一般开始于down事件结束于up或者cancel)。deviceId、source、displayId都是down事件对应的设备信息。split表示当前屏幕上AMOTION_EVENT_ACTION_POINTER_DOWN事件应当当做一个事件序列的开始来处理。当前屏幕上有接受input事件的窗口包含WindowManager.FLAG_SPLIT_TOUCH标志,就会设置split为true。 FLAG_SPLIT_TOUCH这个flag的描述如下:
frameworks/base/core/java/android/view/WindowManager.java
/** Window flag: when set the window will accept for touch events* outside of its bounds to be sent to other windows that also* support split touch. When this flag is not set, the first pointer* that goes down determines the window to which all subsequent touches* go until all pointers go up. When this flag is set, each pointer* (not necessarily the first) that goes down determines the window* to which all subsequent touches of that pointer will go until that* pointer goes up thereby enabling touches with multiple pointers* to be split across multiple windows.*/public static final int FLAG_SPLIT_TOUCH = 0x00800000;
也就是说AMOTION_EVENT_ACTION_POINTER_DOWN(支持多点触控的屏幕第二个手指落下就会上报该事件)事件将会重新选择派发的window。
回到findTouchedWindowTargetsLocked函数,首先1155-1159行代码找到当前屏幕处理事件序列的状态,保存在mTempTouchState中。
1161-1172行代码isSplit表示该屏幕上有可以接收input事件的window是否设置了WindowManager.FLAG_SPLIT_TOUCH标志,如果isSplit为true后面需要为AMOTION_EVENT_ACTION_POINTER_DOWN事件寻找新的目标window处理。switchedDevice表示新的输入设备的事件,此display应该处理不了,需要丢掉该事件。isHoverAction表示是否是悬停设备(如鼠标)指针的移入移出事件。newGesture表示新的事件序列开始,对于第一个手指落到该设备的情况(ACTION_DOWN),或者鼠标移入移除,或者开始滚动滚轮设备,都被认为是新的事件序列开始。
1173-1190行代码在一个新输入序列的开始的情况下, 如果发生了设备切换,也就是当前display无法处理这个事件,那么直接直接丢掉这个事件。否则的话新事件序列的开始要重置屏幕的TouchState,1184-1189行代码就是用于重置该屏幕的TouchState。
下面1192-1341行代码,分为两个case来找到处理查找处理事件的目标window,case 1的代码为1192-1284行包含两种情况会进入该case1处理:
- 一个事件序列的开始:hover 、 scroll、AMOTION_EVENT_ACTION_DOWN事件。
- 拆分多点触控事件的情况下AMOTION_EVENT_ACTION_POINTER_DOWN事件。
case2的代码为1286-1341行,case2表示该事件序列的前边inpu事件已经找到目标处理窗口,后续事件也应该使用该窗口来处理。包含两种情况会进入该case2处理:
- mov、up、cancel 事件。
- 不拆分多点触控事件的情况下AMOTION_EVENT_ACTION_POINTER_DOWN事件。
其实case1对应的情况是要寻找新的window作为事件处理的window,而case2的情况是直接使用case1中找到的window作为目标窗口。case1对应的事件为一个事件序列的开始,case2对应事件序列开始之后后续事件的处理。
下面我们就按照这两个case,首先来看case1的处理:
1193 /* Case 1: New splittable pointer going down, or need target for hover or scroll. */
1194
1195 int32_t pointerIndex = getMotionEventActionPointerIndex(action);
1196 int32_t x = int32_t(entry->pointerCoords[pointerIndex].
1197 getAxisValue(AMOTION_EVENT_AXIS_X));
1198 int32_t y = int32_t(entry->pointerCoords[pointerIndex].
1199 getAxisValue(AMOTION_EVENT_AXIS_Y));
1200 sp<InputWindowHandle> newTouchedWindowHandle;
1201 bool isTouchModal = false;
1202
1203 // Traverse windows from front to back to find touched window and outside targets.
1204 size_t numWindows = mWindowHandles.size();
1205 for (size_t i = 0; i < numWindows; i++) {1206 sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
1207 const InputWindowInfo* windowInfo = windowHandle->getInfo();
1208 if (windowInfo->displayId != displayId) {1209 continue; // wrong display
1210 }
1211
1212 int32_t flags = windowInfo->layoutParamsFlags;
1213 if (windowInfo->visible) { // 窗口可见
1214 if (! (flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) { // 窗口可以接收touch事件
1215 isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
1216 | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
1217 if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) { // 该窗口可以消费掉所有后面窗口的事件 或者 // 点击到了窗口区可接收触摸事件区域 那么该窗口为前台窗口,后面的窗口将无法接收// touch event。保存在newTouchedWindowHandle中,该窗口为事件处主理窗口,后面// 我们叫它前台窗口( foreground window)
1218 newTouchedWindowHandle = windowHandle;
1219 break; // found touched window, exit window loop
1220 }
1221 }
1222
1223 if (maskedAction == AMOTION_EVENT_ACTION_DOWN
1224 && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {// 在事件处理窗口之上的窗口,如果设置了FLAG_WATCH_OUTSIDE_TOUCH标志,则对于// AMOTION_EVENT_ACTION_DOWN事件,需要发送给该窗口一个ACTION_OUTSIDE或者// ACTION_PARTIALLY_OBSCURED事件通知该窗口点击其他窗口的通知。
1225 int32_t outsideTargetFlags = InputTarget::FLAG_DISPATCH_AS_OUTSIDE;
1226 if (isWindowObscuredAtPointLocked(windowHandle, x, y)) {1227 outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
1228 } else if (isWindowObscuredLocked(windowHandle)) {1229 outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
1230 }
1231 // 添加该窗口到mTempTouchState,用于后续事件派发
1232 mTempTouchState.addOrUpdateWindow(
1233 windowHandle, outsideTargetFlags, BitSet32(0));
1234 }
1235 }
1236 }
1237
1238 // Figure out whether splitting will be allowed for this window.
1239 if (newTouchedWindowHandle != NULL
1240 && newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {1241 // New window supports splitting.// 前台窗口包含需要拆分多点触控事件,设置isSplit为真
1242 isSplit = true;
1243 } else if (isSplit) {1244 // New window does not support splitting but we have already split events.
1245 // Ignore the new window.// 新找到的前台窗口不能处理拆分多点触控事件,则设置为null
1246 newTouchedWindowHandle = NULL;
1247 }
1248
1249 // Handle the case where we did not find a window.
1250 if (newTouchedWindowHandle == NULL) {1251 // Try to assign the pointer to the first foreground window we find, if there is one.// 如果没有找到前台窗口,则尝试使用之前的前台窗口,没有的话则拒绝派发该事件
1252 newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle();
1253 if (newTouchedWindowHandle == NULL) {1254 ALOGI("Dropping event because there is no touchable window at (%d, %d).", x, y);
1255 injectionResult = INPUT_EVENT_INJECTION_FAILED;
1256 goto Failed;
1257 }
1258 }
1259 // 设置targetFlags表示该窗口需要发送什么事件给应用。
1260 // Set target flags.
1261 int32_t targetFlags = InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS;
1262 if (isSplit) {1263 targetFlags |= InputTarget::FLAG_SPLIT;
1264 }
1265 if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {1266 targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
1267 } else if (isWindowObscuredLocked(newTouchedWindowHandle)) {1268 targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
1269 }
1270
1271 // Update hover state.
1272 if (isHoverAction) {1273 newHoverWindowHandle = newTouchedWindowHandle;
1274 } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {1275 newHoverWindowHandle = mLastHoverWindowHandle;
1276 }
1277
1278 // Update the temporary touch state.
1279 BitSet32 pointerIds;
1280 if (isSplit) {1281 uint32_t pointerId = entry->pointerProperties[pointerIndex].id;
1282 pointerIds.markBit(pointerId);
1283 }// 添加前台窗口和targetFlags到mTempTouchState用于后续事件派发
1284 mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
针对case 1, 会按照z-order自上而下的遍历window来寻找处理input事件的目标window,要成为目标window要同时满足如下条件:
- window 可见
- window不包含InputWindowInfo::FLAG_NOT_TOUCHABLE标志。
- 点击动作落在了该window区域 或者 该window没有设置FLAG_NOT_FOCUSABLE 和InputWindowInfo::FLAG_NOT_TOUCH_MODAL标志。
条件1 中InputWindowInfo::FLAG_NOT_TOUCHABLE为真表示wndow不能处理touch事件。所以input系统不会把MotionEvent 事件发送给这种类型的window。 在满足条件1 的前提下点击坐标落到window上一定是目标window。另外如果window的标志同时不包含InputWindowInfo::FLAG_NOT_FOCUSABLE| InputWindowInfo::FLAG_NOT_TOUCH_MODAL 标志则该window可以处理没有落在自己可点击区域上面的事件,这种情况input事件也会发送到这个window处理。注意如果找到这样的window,后面的window就不去看了,因为事件不需要派发给后面的window了。我们后面称该window为前台window。
/** Window flag: even when this window is focusable (its* {@link #FLAG_NOT_FOCUSABLE} is not set), allow any pointer events* outside of the window to be sent to the windows behind it. Otherwise* it will consume all pointer events itself, regardless of whether they* are inside of the window. */public static final int FLAG_NOT_TOUCH_MODAL = 0x00000020;
代码1223-1235行代码,在前台window之上,并且设置了 InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH标志的window,它们关心点击自身外部区域 的input事件。点击了它们后面window的区域手指落下的时候需要发送给它们一个ACTION_OUTSIDE事件或者 ACTION_PARTIALLY_OBSCURED 事件。所以当input事件类型为AMOTION_EVENT_ACTION_DOWN时,需要把这些window挑出来放到mTempTouchState中,并且设置上InputTarget::FLAG_WINDOW_IS_OBSCURED或者InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED标志。
这些window最终会收到相关的input事件。
最后1239-1282行代码对目标window做一些处理添后将该window添加到TouchState中。添加window到TouchState中使用的方法为
mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);void InputDispatcher::TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,int32_t targetFlags, BitSet32 pointerIds) {if (targetFlags & InputTarget::FLAG_SPLIT) {split = true;}for (size_t i = 0; i < windows.size(); i++) {TouchedWindow& touchedWindow = windows.editItemAt(i);if (touchedWindow.windowHandle == windowHandle) {touchedWindow.targetFlags |= targetFlags;if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS;}touchedWindow.pointerIds.value |= pointerIds.value;return;}}windows.push();TouchedWindow& touchedWindow = windows.editTop();touchedWindow.windowHandle = windowHandle;touchedWindow.targetFlags = targetFlags;touchedWindow.pointerIds = pointerIds;
}
addOrUpdateWindow 有三个参数,newTouchedWindowHandle表示处理MontionEvent的窗口, targetFlags描述了该窗口该如何处理该MotionEvent,比如InputTarget::FLAG_WINDOW_IS_OBSCURED标志会给该window发送一个ACTION_OUTSIDE事件 。pointerIds 描述了落在该window上多点触控的点。
看完findTouchedWindowTargetsLocked的case1的处理,再来看下case2 的处理,由于case2的目标window比较明确代码也比case1要简单不少:
1286 /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
1287
1288 // If the pointer is not currently down, then ignore the event.
1289 if (! mTempTouchState.down) {// 没有down事件无法进入该状态,move,up,cacel 和 point action都需要TouchStat先进入down状态。
1290 #if DEBUG_FOCUS
1291 ALOGW("Dropping event because the pointer is not down or we previously "
1292 "dropped the pointer down event.");
1293 #endif
1294 injectionResult = INPUT_EVENT_INJECTION_FAILED;
1295 goto Failed;
1296 }
1297
1298 // Check whether touches should slip outside of the current foreground window.
1299 if (maskedAction == AMOTION_EVENT_ACTION_MOVE
1300 && entry->pointerCount == 1
1301 && mTempTouchState.isSlippery()) {// 对于支持拆分多点触控事件的window,需要检查移动事件是否将坐标移动到了目标window之外,// 如果移动到了目标window之外,需要给新的处理该事件的window发送一个ACTION_DOWN事件
1302 int32_t x = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
1303 int32_t y = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
1304
1305 sp<InputWindowHandle> oldTouchedWindowHandle =
1306 mTempTouchState.getFirstForegroundWindowHandle();
1307 sp<InputWindowHandle> newTouchedWindowHandle =
1308 findTouchedWindowAtLocked(displayId, x, y);
1309 if (oldTouchedWindowHandle != newTouchedWindowHandle
1310 && newTouchedWindowHandle != NULL) {// 处理该事件的window发生变化,就的window需要接收ACTION_CANCEL事件,给旧的window的// target设置InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT标志,将会发送ACTION_CANCEL// 事件给它
1311 #if DEBUG_FOCUS
1312 ALOGW("Touch is slipping out of window %s into window %s.",
1313 oldTouchedWindowHandle->getName().string(),
1314 newTouchedWindowHandle->getName().string());
1315 #endif
1316 // Make a slippery exit from the old window.
1317 mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
1318 InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, BitSet32(0));
1319 // 设置新的前台窗口FLAG_DISPATCH_AS_SLIPPERY_ENTER标志将是它收到// ACTION_DOWN事件。
1320 // Make a slippery entrance into the new window.
1321 if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {1322 isSplit = true;
1323 }
1324
1325 int32_t targetFlags = InputTarget::FLAG_FOREGROUND
1326 | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER;
1327 if (isSplit) {1328 targetFlags |= InputTarget::FLAG_SPLIT;
1329 }
1330 if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {1331 targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
1332 }
1333
1334 BitSet32 pointerIds;
1335 if (isSplit) {1336 pointerIds.markBit(entry->pointerProperties[0].id);
1337 }
1338 mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
1339 } // 其他情况使用现有mTempTouchState中保存的目标window
1340 }
1341 }
case2 的代码比较简单,1289-1296 行代码对于还没有接收down事件就进到case2的情况是不正常的,也就是事件序列还没开始,就要处理后续事件,所以这里直接拒绝派发该事件。1302-1338行代码大多数情况都不会被执行到,所以默认是使用case 1中计算好的mTempTouchState中的目标窗口来处理事件。
对于1302-1338行代码代码,支持拆分多点触控事件的window,如果要处理的事件是ACTION_MOVE事件,并且该事件落下的点只有一个,那么这种情况下如果ACTION_MOVE事件如果移出了目标window,则需要给目标window发送一个ACTION_CANCEL事件,表示该事件移除了window,还要找到能处理该事件的下一个window,如果可以找的到,则需要将ACTION_MOVE事件变为ACTION_DOWN事件,防止接收方直接收到ACTION_MOVE事件无法处理。
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER 标志会将给window发送一个ACTION_DOWN(这里把ACTION_MOVE变为ACTION_DOWN),InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT标志会将给window发送一个ACTION_DOWN(这里把ACTION_MOVE变为ACTION_CANCEL),
处理完case1和case2基本上目标window就找到了,下面做一些额外的处理
1342
1343 if (newHoverWindowHandle != mLastHoverWindowHandle) {1344 // Let the previous window know that the hover sequence is over.
1345 if (mLastHoverWindowHandle != NULL) {1346 #if DEBUG_HOVER
1347 ALOGD("Sending hover exit event to window %s.",
1348 mLastHoverWindowHandle->getName().string());
1349 #endif// 处理光标事件的window发生变化,要给旧的window一个ACTION_HOVER_EXIT事件
1350 mTempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
1351 InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
1352 }
1353
1354 // Let the new window know that the hover sequence is starting.
1355 if (newHoverWindowHandle != NULL) {1356 #if DEBUG_HOVER
1357 ALOGD("Sending hover enter event to window %s.",
1358 newHoverWindowHandle->getName().string());
1359 #endif// 需要给新的光标处理窗口一个ACTION_HOVER_ENTER事件。
1360 mTempTouchState.addOrUpdateWindow(newHoverWindowHandle,
1361 InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER, BitSet32(0));
1362 }
1363 }
1364
1365 // Check permission to inject into all touched foreground windows and ensure there
1366 // is at least one touched foreground window.
1367 {1368 bool haveForegroundWindow = false;
1369 for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {1370 const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
1371 if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {1372 haveForegroundWindow = true;// 权限检查(处理inject event的权限),没有权限再该事件不能派发。
1373 if (! checkInjectionPermission(touchedWindow.windowHandle,
1374 entry->injectionState)) {1375 injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
1376 injectionPermission = INJECTION_PERMISSION_DENIED;
1377 goto Failed;
1378 }
1379 }
1380 }
1381 if (! haveForegroundWindow) {// 没有前台窗口,也应该决绝派发该事件
1382 #if DEBUG_FOCUS
1383 ALOGW("Dropping event because there is no touched foreground window to receive it.");
1384 #endif
1385 injectionResult = INPUT_EVENT_INJECTION_FAILED;
1386 goto Failed;
1387 }
1388
1389 // Permission granted to injection into all touched foreground windows.
1390 injectionPermission = INJECTION_PERMISSION_GRANTED;
1391 }
1392
1393 // Check whether windows listening for outside touches are owned by the same UID. If it is
1394 // set the policy flag that we will not reveal coordinate information to this window.
1395 if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {// 如果能处理该input事件的其他窗口与前台窗口不在相同应用进程,则不给它们发送坐标信息,只发送事件。// 设置了FLAG_ZERO_COORDS标志的目标窗口收到的事件坐标都是0,0
1396 sp<InputWindowHandle> foregroundWindowHandle =
1397 mTempTouchState.getFirstForegroundWindowHandle();
1398 const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
1399 for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {1400 const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
1401 if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {1402 sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle;
1403 if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) {1404 mTempTouchState.addOrUpdateWindow(inputWindowHandle,
1405 InputTarget::FLAG_ZERO_COORDS, BitSet32(0));
1406 }
1407 }
1408 }
1409 }
1410
1411 // Ensure all touched foreground windows are ready for new input.
1412 for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {1413 const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
1414 if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {// 检查前台窗口是否准备好处理事件,如果没准备好调到Unresponsive标号处执行。
1415 // Check whether the window is ready for more input.
1416 String8 reason = checkWindowReadyForMoreInputLocked(currentTime,
1417 touchedWindow.windowHandle, entry, "touched");
1418 if (!reason.isEmpty()) {// 处理前台window没有准备好的情况,这里可能上报anr
1419 injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
1420 NULL, touchedWindow.windowHandle, nextWakeupTime, reason.string());
1421 goto Unresponsive;
1422 }
1423 }
1424 }
1425
1426 // If this is the first pointer going down and the touched window has a wallpaper
1427 // then also add the touched wallpaper windows so they are locked in for the duration
1428 // of the touch gesture.
1429 // We do not collect wallpapers during HOVER_MOVE or SCROLL because the wallpaper
1430 // engine only supports touch events. We would need to add a mechanism similar
1431 // to View.onGenericMotionEvent to enable wallpapers to handle these events.
1432 if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {// 如果前台窗口使用了墙纸,那么墙纸顶用需要收到后续ACTION_PARTIALLY_OBSCURED事件,做一些特效处理。
1433 sp<InputWindowHandle> foregroundWindowHandle =
1434 mTempTouchState.getFirstForegroundWindowHandle();
1435 if (foregroundWindowHandle->getInfo()->hasWallpaper) {1436 for (size_t i = 0; i < mWindowHandles.size(); i++) {1437 sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
1438 const InputWindowInfo* info = windowHandle->getInfo();
1439 if (info->displayId == displayId
1440 && windowHandle->getInfo()->layoutParamsType
1441 == InputWindowInfo::TYPE_WALLPAPER) {1442 mTempTouchState.addOrUpdateWindow(windowHandle,
1443 InputTarget::FLAG_WINDOW_IS_OBSCURED
1444 | InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED
1445 | InputTarget::FLAG_DISPATCH_AS_IS,
1446 BitSet32(0));
1447 }
1448 }
1449 }
1450 }
1451
1452 // Success! Output targets.
1453 injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
1454 // 将收集到的事件处理窗口添加到 inputTargets的集合中,用于结果输出。
1455 for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {1456 const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i);
1457 addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
1458 touchedWindow.pointerIds, inputTargets);
1459 }
1460
1461 // Drop the outside or hover touch windows since we will not care about them
1462 // in the next iteration.// filterNonAsIsTouchWindows函数清除那些不需要再跟踪后续事件的窗口。
1463 mTempTouchState.filterNonAsIsTouchWindows();
1464
1465 Failed:
1466 // Check injection permission once and for all.// 检查注入权限
1467 if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {1468 if (checkInjectionPermission(NULL, entry->injectionState)) {1469 injectionPermission = INJECTION_PERMISSION_GRANTED;
1470 } else {1471 injectionPermission = INJECTION_PERMISSION_DENIED;
1472 }
1473 }
1474
1475 // Update final pieces of touch state if the injector had permission.
1476 if (injectionPermission == INJECTION_PERMISSION_GRANTED) {1477 if (!wrongDevice) {1478 if (switchedDevice) {1479 #if DEBUG_FOCUS
1480 ALOGW("Conflicting pointer actions: Switched to a different device.");
1481 #endif// 切换设备导致事件冲突
1482 *outConflictingPointerActions = true;
1483 }
1484
1485 if (isHoverAction) {1486 // Started hovering, therefore no longer down.
1487 if (oldState && oldState->down) {// 发生事件冲突,指针已经按下又收到了指针悬停事件
1488 #if DEBUG_FOCUS
1489 ALOGW("Conflicting pointer actions: Hover received while pointer was down.");
1490 #endif
1491 *outConflictingPointerActions = true;
1492 }
1493 mTempTouchState.reset();
1494 if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER
1495 || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {1496 mTempTouchState.deviceId = entry->deviceId;
1497 mTempTouchState.source = entry->source;
1498 mTempTouchState.displayId = displayId;// 事件序列开始,更新设备信息
1499 }
1500 } else if (maskedAction == AMOTION_EVENT_ACTION_UP
1501 || maskedAction == AMOTION_EVENT_ACTION_CANCEL) {1502 // All pointers up or canceled.
1503 mTempTouchState.reset();// AMOTION_EVENT_ACTION_UP 和 AMOTION_EVENT_ACTION_CANCEL表示事件序列已经结束// 清空mTempTouchState
1504 } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {// 点击事件序列开始
1505 // First pointer went down.
1506 if (oldState && oldState->down) {1507 #if DEBUG_FOCUS
1508 ALOGW("Conflicting pointer actions: Down received while already down.");
1509 #endif// 事件冲突
1510 *outConflictingPointerActions = true;
1511 }
1512 } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {1513 // One pointer went up.// 多点触控指针抬起
1514 if (isSplit) {// 清空处理该事件序列的window
1515 int32_t pointerIndex = getMotionEventActionPointerIndex(action);
1516 uint32_t pointerId = entry->pointerProperties[pointerIndex].id;
1517
1518 for (size_t i = 0; i < mTempTouchState.windows.size(); ) {1519 TouchedWindow& touchedWindow = mTempTouchState.windows.editItemAt(i);
1520 if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {1521 touchedWindow.pointerIds.clearBit(pointerId);
1522 if (touchedWindow.pointerIds.isEmpty()) {1523 mTempTouchState.windows.removeAt(i);
1524 continue;
1525 }
1526 }
1527 i += 1;
1528 }
1529 }
1530 }
1531
1532 // Save changes unless the action was scroll in which case the temporary touch
1533 // state was only valid for this one action.
1534 if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {// 更新该屏幕上的TouchState
1535 if (mTempTouchState.displayId >= 0) {1536 if (oldStateIndex >= 0) {1537 mTouchStatesByDisplay.editValueAt(oldStateIndex).copyFrom(mTempTouchState);
1538 } else {1539 mTouchStatesByDisplay.add(displayId, mTempTouchState);
1540 }
1541 } else if (oldStateIndex >= 0) {1542 mTouchStatesByDisplay.removeItemsAt(oldStateIndex);
1543 }
1544 }
1545
1546 // Update hover state.
1547 mLastHoverWindowHandle = newHoverWindowHandle;
1548 }
1549 } else {1550 #if DEBUG_FOCUS
1551 ALOGW("Not updating touch focus because injection was denied.");
1552 #endif
1553 }
1554
1555 Unresponsive:
1556 // Reset temporary touch state to ensure we release unnecessary references to input channels.// TouchState已经更新到mTouchStatesByDisplay中,这里恢复mTempTouchState
1557 mTempTouchState.reset();
1558
1559 nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);// 更新统计数据
1560 updateDispatchStatisticsLocked(currentTime, entry,
1561 injectionResult, timeSpentWaitingForApplication);
1562 #if DEBUG_FOCUS
1563 ALOGW("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, "
1564 "timeSpentWaitingForApplication=%0.1fms",
1565 injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
1566 #endif// 返回最终结果。
1567 return injectionResult;
1568 }
前边case1和case2已经将可以处理相关事件的window都添加到了mTempTouchState中,后续再做一些通用处理。1343-1363行代码如果hover事件处理窗口变化,需要发送给旧的处理hover事件window一个ACTION_HOVER_EXIT事件,这是通过设置InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT 标志来完成的。新的处理hover事件的window需要接收一个ACTION_HOVER_ENTER事件,这是通过设置一个InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER标志来完成的。
1369-1380行代码检查注入事件能否发送给目标应用,主要通过PhoneWindowManager来进行权限检查。
1395-1409行代码如果能接收input的window和前台window不在相同的应用进程中,则后续发给给它的事件将不包含真实坐标,通过设置InputTarget::FLAG_ZERO_COORDS标志来实现的。
1412-1424行代码检查前台窗口有没有准备好处理input事件,如果没有准备好就会调用handleTargetsNotReadyLocked函数来处理,注意应用anr事件也是在这里上报的。
1432-1450 行代码如果前台窗口需要显示墙纸,那么需要发送给墙纸窗口ACTION_PARTIALLY_OBSCURED事件,来实现一些特效。所以给墙纸窗口添加一个InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED标志。
1455-1459行代码将收集到的接收input事件的窗口添加到输出参数inputTargets中,用于给上层函数进行事件派发。
1643行代码 filterNonAsIsTouchWindows函数清除那些不需要再跟踪后续事件的窗口。
void InputDispatcher::TouchState::filterNonAsIsTouchWindows() {for (size_t i = 0 ; i < windows.size(); ) {TouchedWindow& window = windows.editItemAt(i);if (window.targetFlags & (InputTarget::FLAG_DISPATCH_AS_IS| InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER)) {window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK;window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS;i += 1;} else {windows.removeAt(i);}}
}
filterNonAsIsTouchWindows函数如果一个处理事件没有FLAG_DISPATCH_AS_IS标志或者FLAG_DISPATCH_AS_SLIPPERY_ENTER标志,那么该window不需要处理后续事件,就直接删除。后续MOVE, UP, CANCEL等事件将不再发送给该窗口处理。另外除了FLAG_DISPATCH_AS_IS标志会保留外其他标志也会被清除。也就是说后续的ACTION_PARTIALLY_OBSCURED,ACTION_OBSCURED事件也无法收到(只能收到down的事件)。
1500-1503行代码AMOTION_EVENT_ACTION_UP 和AMOTION_EVENT_ACTION_CANCEL事件,表示一个事件序列结束,所以清除到mTempTouchState中保存的事件序列信息。
1512-1530行代码AMOTION_EVENT_ACTION_POINTER_UP事件,如果拆分touch事件的情况,代表处理该事件序列也已经结束,清除对应处理该事件的window。
1534-1544行代码更新TouchStat到mTouchStatesByDisplay。
1557行 TouchState已经更新到mTouchStatesByDisplay中,这里重置mTempTouchState
1559-1565行做一些统计工作,最终返回injectionResult,表示该如何处理该事件。
收集到处理事件的window后,最终事件通过dispatchEventLocked(currentTime, entry, inputTargets)函数派发, dispatchEventLocked函数又会调用prepareDispatchCycleLocked函数, prepareDispatchCycleLocked函数调用enqueueDispatchEntriesLocked函数来将事件转化为发送给应用窗口的对应事件。
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {bool wasEmpty = connection->outboundQueue.isEmpty();// Enqueue dispatch entries for the requested modes.enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_OUTSIDE);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_IS);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);// If the outbound queue was previously empty, start the dispatch cycle going.if (wasEmpty && !connection->outboundQueue.isEmpty()) {startDispatchCycleLocked(currentTime, connection);}
}
对于不同inputTargetFlage会产生不同的事件派发给window。有
InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT,InputTarget::FLAG_DISPATCH_AS_OUTSIDE,InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER,InputTarget::FLAG_DISPATCH_AS_IS,InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT,InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER前边我们已经介绍过了。
void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,int32_t dispatchMode) {int32_t inputTargetFlags = inputTarget->flags;if (!(inputTargetFlags & dispatchMode)) { // 如果targetFlag不包含dispatchMode则不会产生相应的事件,直接返回。return;}inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode;// This is a new event.// Enqueue a new dispatch entry onto the outbound queue for this connection.DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments refinputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,inputTarget->scaleFactor);// Apply target flags and update the connection's input state.switch (eventEntry->type) {case EventEntry::TYPE_KEY: {KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);dispatchEntry->resolvedAction = keyEntry->action;dispatchEntry->resolvedFlags = keyEntry->flags;if (!connection->inputState.trackKey(keyEntry,dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {#if DEBUG_DISPATCH_CYCLEALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",connection->getInputChannelName());
#endifdelete dispatchEntry;return; // skip the inconsistent event}break;}case EventEntry::TYPE_MOTION: {MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT;} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) {dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL;} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;} else {dispatchEntry->resolvedAction = motionEntry->action;}if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE&& !connection->inputState.isHovering(motionEntry->deviceId, motionEntry->source, motionEntry->displayId)) {#if DEBUG_DISPATCH_CYCLEALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter event",connection->getInputChannelName());
#endifdispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;}dispatchEntry->resolvedFlags = motionEntry->flags;if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;}if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED) {dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;}if (!connection->inputState.trackMotion(motionEntry,dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {#if DEBUG_DISPATCH_CYCLEALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion event",connection->getInputChannelName());
#endifdelete dispatchEntry;return; // skip the inconsistent event}break;}}// Remember that we are waiting for this dispatch to complete.if (dispatchEntry->hasForegroundTarget()) {incrementPendingForegroundDispatchesLocked(eventEntry);}// Enqueue the dispatch entry.connection->outboundQueue.enqueueAtTail(dispatchEntry);traceOutboundQueueLengthLocked(connection);
}
enqueueDispatchEntryLocked函数根据目标窗口的targetFlag和dispatchMode来设置事件的dispatchEntry->resolvedAction,也就是要发送给该窗口的事件类型。
Android InputDispatch事件派发->选择目标窗口相关推荐
- Android触摸事件派发(一) ViewGroup的dispatchTouchEvent()
ViewGroup的派发事件代码主要由dispatchTouchEvent(MotionEvent ev)方法实现,如下 @Overridepublic boolean dispatchTouchEv ...
- android 事件派发流程详解
Android 5.0(Lollipop)事件输入系统(Input System) 2014-12-15 23 个评论 来源:世事难料,保持低调 收藏 我要投稿 其实Androi ...
- 利用勾子监视系统或进程中的各种事件消息,截获发往目标窗口的消息并进行处理
钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统.每当特定 的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得 到控制权.这时钩子函数即可以加工处理 ...
- Android系统(120)-android的事件分发机制
android的事件分发机制 android的事件分发机制 比如说,现在你所在的公司中有一项任务被派发下来了,项目经理把项目交给你的老大,你的老大老大手下有很多人,看了看觉得你做很合适,把这个任务交给 ...
- Android 输入事件一撸到底之View接盘侠(3)
前言 系列文章 1.Android 输入事件一撸到底之源头活水(1) 2.Android 输入事件一撸到底之DecorView拦路虎(2) 3.Android 输入事件一撸到底之View接盘侠(3 前 ...
- 为了讲清楚Android触摸事件,我“拆了部手机”
Android 是一个有用户界面(GUI)的操作系统,在它诞生之初,就是为带有触摸屏的手持设备准备的.作为提供给用户最重要的交互方式之一,了解触摸系统是怎么工作的,对于实际的项目开发有着非常大的帮助. ...
- Android的事件分发
1. Touch事件和绘制事件的异同之处 Touch事件和绘制事件很类似,都是由ViewRoot派发下来的,但是不同之处在绘制事件是由应用中的某个View发起请求,一层一层上传到ViewRoot,再有 ...
- Android 系统(218)---Android的事件分发机制以及滑动冲突的解决
Android的事件分发机制以及滑动冲突的解决 声明: 本文主要涉及VIew的事件分发与滑动冲突的解决,关于View的事件分发流程的部分内容参考自: Android事件分发机制详解:史上最全面.最 ...
- 一文读懂Android View事件分发机制
Android View 虽然不是四大组件,但其并不比四大组件的地位低.而View的核心知识点事件分发机制则是不少刚入门同学的拦路虎.ScrollView嵌套RecyclerView(或者ListVi ...
最新文章
- 《The Art of Readable Code》 读书笔记 01
- 对话高博(二)| 换工作这件事
- 如何在网站建设时正确设置符合SEO优化的元素?
- 100小时学会sap-财务篇fico总结介绍篇
- ai去除水印_ai全自动视频剪辑软件,每天批量制作800条原创视频!
- 学习Java之前先学C语言
- JUC:ConcurrentSkipListMap/ConcurrentSkipListSet(并发容器)
- Atitit Kafka 使用总结 内容 Kafka2.0 50M1 启动 要启动zookeeper 先,比ativemp麻烦很多啊1 Kafka生产者 1 Kafka消费者2 2
- 编译安装nginx并修改版本头信息—参考实例
- 哈理工OJ 1151 追求(斐波那契变形【思维题目】)
- 斗鱼弹幕服务器未响应,斗鱼看不到弹幕的解决方法步骤
- 安徽大学线性代数习题册(第三章详细解答)
- 新手如何Reverces(3自动化逆向篇)
- 一根均线选股法_一条均线走天下,经典实用的均线选股战法,学会让你少走弯路!...
- 北京著名“十大特色美食街”
- sqlDBX 链接 mysql 提示ODBC驱动不正确
- [ CTF ]【天格】战队WriteUp-第六届”蓝帽杯“全国大学生网络安全技能大赛(半决赛)
- python 使用 io.BytesIO 内存文件加速图片生成服务
- 【实用工具系列之爬虫】python实现爬取代理IP(防 ‘反爬虫’)
- Stanford Large-Scale 3D Indoor Spaces Dataset (S3DIS)