InputMonitor注意事项

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

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>

终点。

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

时间: 2024-12-16 11:35:10

InputMonitor注意事项的相关文章

面向对象注意事项

在面向对象中,有实例变量和类变量,实例变量为类对象的实例成员,而类变量不仅类可以直接调用,而且类的对象也可以调用.类对象可以对实例变量进行添加.修改.删除操作等... 下面就用个示例来做参考: #!/usr/bin/env python # -*- coding:utf-8 -*- class PersonInfo(object): commity_data = 123 def __init__(self,name,age): self.name = name self.age = age de

switch使用时有哪些注意事项

switch语句用于多分支选择,在使用switch(expr)的时候,expr只能是一个枚举常量(内部也是由整型或字符类型实现)或一个整数表达式,其中整数表达式可以是基本类型int或其对应的包装类Integer,当然也包括不同的长度整型,例如short.由于byte.short和char都能够被隐式地转换为int类型,因此这些类型以及它们对应的包装类型都可以作为switch的表达式.但是,long.float.double.String类型由于不能够隐式地转换为int类型,因此它们不能被用作sw

Citrix Xen 6.2 在Adaptec raid 6805卡子上的安装注意事项

Installing with Citrix XenServer   Note: To install the Adaptec RAID controller driver with CitrixXenServer, you must burn the XenServer driver iso image to a writeable CD. Youwill be prompted to insert the XenServer driver CD twice.You need the XenS

CloudStack添加新的API注意事项

1.***Cmd上面的@APICommand里的responseObject关联对于的Response: 2.在tomcatconf/command.properties.in添加新API的权限: 3.为得到Cmd,都需要实现PluggableService接口的getCommands()方法,核心Cmd都需在ManagementServerImpl类的getComands()方法里将新添加的***Cmd类添加到cmList中. CloudStack添加新的API注意事项,码迷,mamicode

开发APP注意事项

开发APP的时候要注意的一些事项?随着互联网的高速发展,移动APP也如雨后春笋般快速发展.营销战略也逐渐从PC端转为移动端,开创新的营销道路--移动营销.面对移动APP的火热,越来越多的企业和商家们重视移动营销这一块,也迫切开发属于自己的企业APP. 移动开发大师表示:"我们在热衷开发APP的同时也要注意一些事项,以免造成资源的浪费."下面让麦子学院老师来告诉你,当你在开发APP的时候要注意的一些事项: 首先,如果你要开发APP首先第一件事就是做好充分的市场调查,明确知道你要开发什么样

复合索引的优点和注意事项

概念:     单一索引是指索引列为一列的情况,即新建索引的语句只实施在一列上;     用户可以在多个列上建立索引,这种索引叫做复合索引(组合索引);     复合索引在数据库操作期间所需的开销更小,可以代替多个单一索引;     同时有两个概念叫做窄索引和宽索引,窄索引是指索引列为1-2列的索引,宽索引也就是索引列超过2列的索引;     设计索引的一个重要原则就是能用窄索引不用宽索引,因为窄索引往往比组合索引更有效; 使用:     创建索引     create index idx1 o

Jetson TX2安装Jetpack 3.0注意事项

Jetson TX2安装Jetpack 3.0注意事项 nvidia jetson tx2配置caffe: http://blog.csdn.net/jiongnima/article/details/70040262 CSDN jiongnima博主的这篇文章非常详细完整,但是作为一个初次接触nvidia jetson TX2的人来说,还是会难免遇到一些困难,下面我在这篇文章的基础上,写一些我在安转Jetpack 3.0时遇到的问题,希望大家可以坐在以后的安装过程中规避这些问题:  1.在nv

Dockerfile注意事项

准则 尽量将Dockerfile放在空目录中,如果目录中必须有其他文件,则使用.dockerignore文件. 避免安装不必须的包. 每个容器应该只关注一个功能点. 最小化镜像的层数. 多行参数时应该分类.这样更清晰直白,便于阅读和review,另外,在每个换行符\前都增加一个空格. 对构建缓存要有清楚的认识. 指令注意事项 FROM Dockerfile reference for the FROM instruction 任何时候,尽量使用官方镜像源作为你镜像的基础镜像.我们建议使用Debi

购买家电注意事项

在预算有限情况下,如何购买到更具性价比的家电. 1 在5.1.10.1等节日购买,或者在苏宁.京东等几周年纪念日做活动时购买. 2 尽量要在正规渠道.大商场购买,并且购买有品牌的家电. 第二部分 分类家电的购买注意事项 1 热水器 先谈好热水器的价格,并且最后要询问是否拆旧.是否免费安装.安装过程中是否有其他收费事项(比如接出来的软管) 我买了A.O史密斯的热水器,安装师傅帮忙安装了一下,配了两根软管和水阀,还有固定架子,就收了300块钱. 如果事先知道,那销售签订合同时,肯定还可以谈价格. 2