文章只记录自己的点点理解。为了你自己的参考。

1、mInputFocus

WMS.addWindow()-->WMS.finishUpdateFocusedWindowAfterAssignLayersLocked()-->InputMonitor.setInputFocusLw()-->mInputFocus = newWindow;

add一个window的时候会又一次寻找焦点窗体。并把焦点窗体保存在WMS.mCurrentFocus中。这个焦点窗体也会保存到InputMonitor.mInputFocus 中。关键代码:

<span style="font-size:18px;">addWindow(){
……
boolean focusChanged = false;
if (win.canReceiveKeys()) {
focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
false /*updateInputWindows*/);
if (focusChanged) {
imMayMove = false;
}
} if (imMayMove) {
moveInputMethodWindowsIfNeededLocked(false);
} assignLayersLocked(displayContent.getWindowList());
// Don't do layout here, the window must call
// relayout to be displayed, so we'll do it there. if (focusChanged) {
finishUpdateFocusedWindowAfterAssignLayersLocked(false /*updateInputWindows*/);
}
mInputMonitor.updateInputWindowsLw(false /*force*/);
…………
}
</span>

这几行代码虽短,逻辑也非常easy,可是调用了非常多方法。对于canReceiveKeys()函数:

<span style="font-size:18px;">public final boolean canReceiveKeys() {
return isVisibleOrAdding()
&& (mViewVisibility == View.VISIBLE)
&& ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0);
}</span>

假设窗体设置了WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE属性,那么该窗体是不能获得焦点的,即canReceiveKeys()会返回false。也就是说焦点窗体是不会更改的。对于悬浮的窗体一般来说是不须要获得焦点的。故一般设置WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE属性,不然会存在一些问题。假设该窗体能够接受key事件(这个key事件有点歧义,并非仅仅接受按键事件),那么就调用updateFocusedWindowLocked()。这个以后再研究。假设焦点窗体发生变更,那么就调用moveInputMethodWindowsIfNeededLocked(false)来移动输入法窗体到合适的位置。

mInputFocus中保存的窗体在调用InputMonitor.updateInputWindowsLw()时终于设置到InputDispatcher.mFocusedWindowHandle中去了。

2、mInputDispatchFrozen

冻结InputDispatcher标志,“ When true, prevents input dispatch from proceeding until set to false again.”。

①WMS.startFreezingDisplayLocked()-->InputMonitor.freezeInputDispatchingLw()-->mInputDispatchFrozen = true; updateInputDispatchModeLw();

②WMS.stopFreezingDisplayLocked()-->InputMonitor.thawInputDispatchingLw()-->mInputDispatchFrozen = false; updateInputDispatchModeLw();-->InputManagerService.setInputDispatchMode()-->InputDispatcher.setInputDispatchMode(bool enabled,
bool frozen)-->mDispatchFrozen = frozen;

<span style="font-size:18px;">void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
nsecs_t currentTime = now(); // Reset the key repeat timer whenever we disallow key events, even if the next event
// is not a key. This is to ensure that we abort a key repeat if the device is just coming
// out of sleep.
if (!mPolicy->isKeyRepeatEnabled()) {
resetKeyRepeatLocked();
} // If dispatching is frozen, do not process timeouts or try to deliver any new events.
if (mDispatchFrozen) {
#if DEBUG_FOCUS
ALOGD("Dispatch frozen. Waiting some more.");
#endif
return;
}
……….
}
</span>

调用dispatchOnceInnerLocked()分发事件时,直接return掉。上面的调用逻辑能够知道冻结/解冻显示屏时,会冻结InputDispatcher的分发流程。

3、mInputDispatchEnabled

一个java层控制InputDispatcher的开关。

①WMS.setEventDispatching()-->InputMonitor.setEventDispatchingLw()

②WMS.performEnableScreen()-->InputMonitor.setEventDispatchingLw()-->mInputDispatchEnabled = enabled;updateInputDispatchModeLw();-->InputManagerService.setInputDispatchMode()-->InputDispatcher.setInputDispatchMode(bool enabled,
bool frozen)-->mDispatchEnabled = enabled;

详细是怎么使能InputDispatcher的?

<span style="font-size:18px;">if (!mDispatchEnabled) {
dropReason = DROP_REASON_DISABLED;
}
</span>

在dispatchOnceInnerLocked()中会设置dropReason(放弃原因),假设dropReason不为0表示输入事件将被放弃掉,不会分发。

google源代码是这样解释的:“ When true, input dispatch proceeds normally.  Otherwise all events are dropped. Initially false, so that input does not get dispatched until boot is finished at which point the ActivityManager will enable dispatching.”

4、mUpdateInputWindowsNeeded

是否须要更新窗体信息到InputDispatcher中的标志。在非强制性更新窗体信息到InputDispatcher中去时,必须先调用InputMonitor.setUpdateInputWindowsNeededLw()设置该标志。然后再调用InputMonitor.updateInputWindowsLw()。

假设强制性更新窗体信息到InputDispatcher中去时就不必设置该变量了。

5、notifyInputChannelBroken()

InputManager通知WMS输入通道破裂函数。

<span style="font-size:18px;">    public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
if (inputWindowHandle == null) {
return;
} synchronized (mService.mWindowMap) {
WindowState windowState = (WindowState) inputWindowHandle.windowState;
if (windowState != null) {
Slog.i(WindowManagerService.TAG, "WINDOW DIED " + windowState);
mService.removeWindowLocked(windowState.mSession, windowState);
}
}
}
</span>

函数处理逻辑很easy。就是将输入通道破裂的窗体直接调用removeWindowLocked()进行移除。

调用流程:InputDispatcher.startDispatchCycleLocked()--> InputDispatcher.abortBrokenDispatchCycleLocked()--> InputDispatcher.onDispatchCycleBrokenLocked()-->InputDispatcher.doNotifyInputChannelBrokenLockedInterruptible()-->NativeInputManager.notifyInputChannelBroken()-->InputManagerService.notifyInputChannelBroken()-->InputMonitor.notifyInputChannelBroken();

startDispatchCycleLocked()中在publish event不成功,就会走以下的逻辑:

<span style="font-size:18px;">        if (status) {
if (status == WOULD_BLOCK) {
if (connection->waitQueue.isEmpty()) {
ALOGE("channel '%s' ~ Could not publish event because the pipe is full. "
"This is unexpected because the wait queue is empty, so the pipe "
"should be empty and we shouldn't have any problems writing an "
"event to it, status=%d", connection->getInputChannelName(), status);
abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
} else {
// Pipe is full and we are waiting for the app to finish process some events
// before sending more events to it.
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ Could not publish event because the pipe is full, "
"waiting for the application to catch up",
connection->getInputChannelName());
#endif
connection->inputPublisherBlocked = true;
}
} else {
ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
"status=%d", connection->getInputChannelName(), status);
abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
}
return;
}
</span>

在正常pipe full的情况下是不会调用abortBrokenDispatchCycleLocked()的,其它情况则调用abortBrokenDispatchCycleLocked()来处理。

<span style="font-size:18px;">void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection, bool notify) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s",
connection->getInputChannelName(), toString(notify));
#endif // Clear the dispatch queues.
drainDispatchQueueLocked(&connection->outboundQueue);
traceOutboundQueueLengthLocked(connection);
drainDispatchQueueLocked(&connection->waitQueue);
traceWaitQueueLengthLocked(connection); // The connection appears to be unrecoverably broken.
// Ignore already broken or zombie connections.
if (connection->status == Connection::STATUS_NORMAL) {
connection->status = Connection::STATUS_BROKEN; if (notify) {
// Notify other system components.
onDispatchCycleBrokenLocked(currentTime, connection);
}
}
}
</span>

函数中清理outboundQueue和waitQueue两个队列中的消息。然后调用onDispatchCycleBrokenLocked()-->.....来通知WMS移除相应的窗体。

outboundQueue:Queue of events that need to be published to the connection.

waitQueue:Queue of events that have been published to the connection but that have not yet received a "finished" response from the application.

输入事件仅仅有收到应用回复的finished消息才算真正分发成功。

6、notifyANR()

源代码对这个函数的凝视已经很清楚:

<span style="font-size:18px;">/* Notifies the window manager about an application that is not responding.
* Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
*
* Called by the InputManager.
</span>

调用流程:InputDispatcher.findFocusedWindowTargetsLocked();InputDispatcher.findTouchedWindowTargetsLocked()-->InputDispatcher.handleTargetsNotReadyLocked()--> InputDispatcher.onANRLocked()-->InputDispatcher.doNotifyANRLockedInterruptible()-->NativeInputManager.notifyANR()-->InputManagerService.notifyANR()-->InputMonitor.notifyANR();

在InputDispatcher的findFocusedWindowTargetsLocked()和findTouchedWindowTargetsLocked()函数中存在多种异常情况是目标窗体窗体还没准备好接受事件的,这时须要调用handleTargetsNotReadyLocked()来处理,handleTargetsNotReadyLocked()中会作出一定时间的等待。假设终于超时那么就会调用onANRLocked()报ANR。

InputDispatcher.notifyANR()函数处理也非常easy,终于会调用ActivityManagerNative.getDefault().inputDispatchingTimedOut(windowState.mSession.mPid, aboveSystem, reason);来拉起一个ANR对话框。

7、interceptKeyBeforeQueueing()、interceptKeyBeforeDispatching()

①InputDispatcher.notifyKey()-->NativeInputManager.interceptKeyBeforeQueueing()--> InputManagerService.interceptKeyBeforeQueueing()-->InputMonitor.interceptKeyBeforeQueueing()-->PhoneWindowManager.interceptKeyBeforeQueueing()

②InputDispatcher.dispatchKeyLocked()-->InputDispatcher.doInterceptKeyBeforeDispatchingLockedInterruptible()-->NativeInputManager.interceptKeyBeforeDispatching()--> InputManagerService.interceptKeyBeforeDispatching()-->InputMonitor.interceptKeyBeforeDispatching()-->PhoneWindowManager.interceptKeyBeforeDispatching()

从函数名及调用流程来看,一个是在入队之前调用,一个是分发时调用。

电源键事件走的就是这两个函数中的一个。

8、interceptMotionBeforeQueueingWhenScreenOff()

InputDispatcher.notifyMotion()-->NativeInputManager.interceptMotionBeforeQueueing()--> InputManagerService.interceptMotionBeforeQueueingWhenScreenOff()-->InputMonitor.interceptMotionBeforeQueueingWhenScreenOff()-->PhoneWindowManager.interceptMotionBeforeQueueingWhenScreenOff()

<span style="font-size:18px;">void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
// Policy:
// - Ignore untrusted events and pass them along.
// - No special filtering for injected events required at this time.
// - Filter normal events based on screen state.
// - For normal events brighten (but do not wake) the screen if currently dim.
if ((policyFlags & POLICY_FLAG_TRUSTED) && !(policyFlags & POLICY_FLAG_INJECTED)) {
if (isScreenOn()) {
policyFlags |= POLICY_FLAG_PASS_TO_USER; if (!isScreenBright()) {
policyFlags |= POLICY_FLAG_BRIGHT_HERE;
}
} else {
JNIEnv* env = jniEnv();
jint wmActions = env->CallIntMethod(mServiceObj,
gServiceClassInfo.interceptMotionBeforeQueueingWhenScreenOff,
policyFlags);
if (checkAndClearExceptionFromCallback(env,
"interceptMotionBeforeQueueingWhenScreenOff")) {
wmActions = 0;
} policyFlags |= POLICY_FLAG_WOKE_HERE | POLICY_FLAG_BRIGHT_HERE;
handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
}
} else {
policyFlags |= POLICY_FLAG_PASS_TO_USER;
}
}</span>

在收到的输入事件来自设备,而不是上层应用注入的事件时,此时假设屏幕是灭屏状态,那么就调用interceptMotionBeforeQueueingWhenScreenOff()函数来处理。

9、pauseDispatchingLw()、resumeDispatchingLw()

这两个函数很easy。就是设置WindowToken.paused。并强制更新到InputDispatcher中去。

<span style="font-size:18px;">    public void pauseDispatchingLw(WindowToken window) {
if (! window.paused) {
if (WindowManagerService.DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Pausing WindowToken " + window);
} window.paused = true;
updateInputWindowsLw(true /*force*/);
}
} public void resumeDispatchingLw(WindowToken window) {
if (window.paused) {
if (WindowManagerService.DEBUG_INPUT) {
Slog.v(WindowManagerService.TAG, "Resuming WindowToken " + window);
} window.paused = false;
updateInputWindowsLw(true /*force*/);
}
}
</span>

在InputDispatcher中。findFocusedWindowTargetsLocked()和 findTouchedWindowTargetsLocked()中寻找到的目标窗体假设状态为paused。那么就会调用handleTargetsNotReadyLocked()进行等待。

<span style="font-size:18px;">if (touchedWindow.windowHandle->getInfo()->paused) {
injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
NULL, touchedWindow.windowHandle, nextWakeupTime,
"Waiting because the touched window is paused.");
goto Unresponsive;
}
</span>

终点。

版权声明:本文博主原创文章。博客,未经同意不得转载。

InputMonitor注意事项的更多相关文章

  1. jQuery UI resizable使用注意事项、实时等比例拉伸及你不知道的技巧

    这篇文章总结的是我在使用resizable插件的过程中,遇到的问题及变通应用的奇思妙想. 一.resizable使用注意事项 以下是我在jsfiddle上写的测试demo:http://jsfiddl ...

  2. Windows Server 2012 NIC Teaming介绍及注意事项

    Windows Server 2012 NIC Teaming介绍及注意事项 转载自:http://www.it165.net/os/html/201303/4799.html Windows Ser ...

  3. TODO:Golang指针使用注意事项

    TODO:Golang指针使用注意事项 先来看简单的例子1: 输出: 1 1 例子2: 输出: 1 3 例子1是使用值传递,Add方法不会做任何改变:例子2是使用指针传递,会改变地址,从而改变地址. ...

  4. app开发外包注意事项,2017最新资讯

    我们见过很多创业者,栽在这app外包上.很多创业者对于app外包这件事情不是特别重视,以为将事情交给app外包公司就完事了,实际上不是的.无论是从选择app外包公司还是签订合同.售后维护等各方面都有许 ...

  5. favicon.ioc使用以及注意事项

    1.效果 2.使用引入方法 2.1 注意事项:(把图标命名为favicon.ico,并且放在根目录下,同时使用Link标签,多重保险) 浏览器默认使用根目录下的favicon.ico 图标(如果你并没 ...

  6. ORACLE分区表梳理系列(二)- 分区表日常维护及注意事项(红字需要留意)

    版权声明:本文发布于http://www.cnblogs.com/yumiko/,版权由Yumiko_sunny所有,欢迎转载.转载时,请在文章明显位置注明原文链接.若在未经作者同意的情况下,将本文内 ...

  7. 【原】Masonry+UIScrollView的使用注意事项

    [原]Masonry+UIScrollView的使用注意事项 本文转载请注明出处 —— polobymulberry-博客园 1.问题描述 我想实现的使用在一个UIScrollView依次添加三个UI ...

  8. 《连载 | 物联网框架ServerSuperIO教程》- 5.轮询通讯模式开发及注意事项。附:网友制作的类库说明(CHM)

    1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...

  9. 《连载 | 物联网框架ServerSuperIO教程》- 6.并发通讯模式开发及注意事项

    1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...

随机推荐

  1. 用NMAKE创建VS2012 C++工程 HelloWorld

    由于需要精通GDAL的源代码,所以还是有必要精通NMAKE,先来尝试创建一个NMAKE工程. 之前一篇文章Windows7中Emacs 24 shell使用Gitbash已经介绍了如何在Emacs的s ...

  2. 《javascript高级编程》读书笔记(三)变量、范围和内存的问题

     第四章:变量.范围和内存的问题 检測类型:typeof是确定一个变量是字符串.数值.布尔值,还是undefined的最佳工具.可是假设变量的值是一个对象或null,typeof仅仅会返回" ...

  3. Linux系统部署规范v1.0

    Linux系统部署规范v1.0 目的: 1.尽可能减少线上操作: 2.尽可能实现自动化部署: 3.尽可能减少安装服务和启动的服务: 4.尽可能使用安全协议提供服务: 5.尽可能让业务系统单一: 6.尽 ...

  4. Python学习入门基础教程(learning Python)--3.3.1 Python下的布尔表达式

    简单的说就是if要判断condition是真是假,Python和C语言一样非0即真,所以如果if的condition是布尔表达式我们可以用True或者非0数(不可是浮点数)表示真,用False或者0表 ...

  5. python学习笔记--for循环

    推荐一个学习语言的网站:http://www.codecademy.com 有教程,可以边学边写,蛮不错的. for循环: 1.for loops allow us to iterate throug ...

  6. HTML CSS——background的认识(一)

    今天回归bug时无意间看到了样式表中background属性,如今总结一下: 1.background-color:设置元素的背景色.其值能够为:color-name.color-rgb.color- ...

  7. Ext4功能和文件系统的简单功能

    Linux kernel 自 2.6.28 開始正式支持新的文件系统 Ext4. Ext4 是 Ext3 的改进版,改动了 Ext3 中部分重要的数据结构,而不只像 Ext3 对 Ext2 那样,不过 ...

  8. UVa 11408 - Count DePrimes

    题目:一个数的素因子的和假设也是素数就叫做DePrimes,统计给定区间内的DePrimes. 分析:数论.本题使用用一种素数的筛法,欧拉筛法,也加线性筛法. 这样的方法,每次删选分两种情况:1.素因 ...

  9. kb3035583

    dism /online /Get-Packages /Format:Table|findstr 3035583 升级到w10补丁

  10. poj 3259(bellman最短路径)

    Wormholes Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 30169   Accepted: 10914 Descr ...