Android Focused Window的更新

 

启动App时更新inputInfo/请求焦点窗口流程:

App主线程调ViewRootImpl.java的relayoutWindow();然后调用到Wms的relayoutWindow(),窗口布局流程。焦点窗口的更新,通过WMS#updateFocusedWindowLocked()方法开始,下面从这个方法开始,看下整个更新流程。

WMS#updateFocusedWindowLocked: 其中,第一个参数mode表示更新焦点窗口时所处的阶段,共有五个参数

// 表示正常更新    
static final int UPDATE_FOCUS_NORMAL = 0;    
// 表示此次更新焦点窗口发生在window layer分配之前    
static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1;    
// 表示此次更新焦点窗口发生在进行放置Surface过程中,在performSurfacePlacement()时    
static final int UPDATE_FOCUS_PLACING_SURFACES = 2;    
// 表示此次更新焦点窗口发生在进行放置Surface之前    
static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3;    
// 表示此次更新焦点窗口发生在焦点窗口移除后    
static final int UPDATE_FOCUS_REMOVING_FOCUS = 4;

如在Window添加过程的addWindow()方法中,更新焦点窗口发生在分配窗口layer流程之前,因此使用UPDATE_FOCUS_WILL_ASSIGN_LAYERS作为第一个参数,表示此次更新时,还没有分配layer。 针对不同阶段,会有不同的操作。第二个参数表示是否同步更新InputWindow,一般在调用的地方会写死

RootWindowContainer#updateFocusedWindowLocked: 这里会遍历DisplayContent,并在每个DisplayContent中进行更新,然后将更新的结果返回给DisplayContent#mCurrentFocus变量,该变量表示全局的焦点窗口。同时更新mTopFocusedDisplayId变量,表示当前焦点屏(即焦点窗口所在的屏)。

DisplayContent#updateFocusedWindowLocked: 上述方法中:

1.通过findFocusedWindowIfNeeded()方法寻找焦点窗口;

2.根据焦点窗口的变化,更新Input Target窗口;

3.更新全局焦点窗口对象mCurrentFocus;

4.根据更新的不同阶段做不同处理。

5.向InputMonitor中设置焦点窗口setInputFocusLw

下面看下如何寻找到焦点窗口。

DisplayContent#findFocusedWindowIfNeeded: 当topFocusedDisplayId为INVALID_DISPLAY时,认为当前焦点display没有焦点窗口,需要寻找重新确认,所以又继续执行findFocusedWindow()方法寻找, 该方法中,会遍历所有WindowState,然后将寻找到的WindowState赋值给mTmpWindow,并返回给WMS。接下来看下这个mFindFocusedWindow函数接口对象

1.如果WindowState不能接收Input事件,则不能作为焦点窗口;

2.如果没有前台Activity,则当前WindowState作为焦点窗口返回;

3.如果前台Activity是不可获焦状态,则当前WindowState作为焦点窗口返回;

4.如果当前WindowState由ActivityRecord管理,且该WindowState不是Staring Window类型,那么当前台Activity在当前WindowState所属Activity之上时,不存在焦点窗口;

5.如果Activity当前嵌入到焦点任务中,则除非 Activity与FocusedApp位于同一TaskFragment上,否则无法获得焦点。

6.如果以上条件都不满足,则当前WindowState作为焦点窗口返回;

接下来看一下canReceiveKeys这个函数。如果一个WindowState可以接受Input事件,需要同时满足多个条件:

1. isVisibleRequestedOrAdding()方法为true,表示该WindowState可见或处于添加过程中:

2. mViewVisibility属性为View.VISIBLE,表示客户端View可见;

3. mRemoveOnExit为false,表示WindowState的退出动画不存在;

4. mAttrs.flags中不存在FLAG_NOT_FOCUSABLE标记,该标记如果设置,表示该窗口为不可获焦窗口;

5. mActivityRecord为null或者mActivityRecord可获焦;

6. shouldIgnoreInput()方法为false,表示可以接受Touch事件。

isVisibleRequestedOrAdding()方法用来判断该WindowState可见或处于添加过程中:

shouldIgnoreInput() 方法用来判断该WindowState是否能够接收touch事件。

如果遇到找不到焦点窗口的情况:比如log发现窗口已经是onresume的状态,但是焦点窗口一直未请求切换到此窗口可以查看如下这条log,主要是打印canReceiveKeys为何false的原因(那个属性不对)

canReceiveKeysReason此方法

/** Returns {@code true} if this window desires key events. */
boolean canReceiveKeys() {return canReceiveKeys(false /* fromUserTouch */);
}public String canReceiveKeysReason(boolean fromUserTouch) {return "fromTouch= " + fromUserTouch+ " isVisibleRequestedOrAdding=" + isVisibleRequestedOrAdding()+ " mViewVisibility=" + mViewVisibility+ " mRemoveOnExit=" + mRemoveOnExit+ " flags=" + mAttrs.flags+ " appWindowsAreFocusable="+ (mActivityRecord == null || mActivityRecord.windowsAreFocusable(fromUserTouch))+ " canReceiveTouchInput=" + canReceiveTouchInput()+ " displayIsOnTop=" + getDisplayContent().isOnTop()+ " displayIsTrusted=" + getDisplayContent().isTrusted()+ " transitShouldKeepFocus=" + (mActivityRecord != null&& mTransitionController.shouldKeepFocus(mActivityRecord));     
}

然后我们回到mFindFocusedWindow接口对象中,其他条件就不一一说明,最终从DisplayContent中自顶向下寻找到的第一个满足条件的窗口,将作为新的焦点窗口后,接下来更新Display#mCurrentFocus变量,表示当前焦点窗口。

@frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
relayoutWindowif (focusMayChange) { if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) }
@frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java    //其中,第一个参数mode表示更新焦点窗口时所处的阶段,共有五个参数:boolean changed = mRoot.updateFocusedWindowLocked(mode, updateInputWindows);// 存储了当前焦点窗口的Pid和ActivityRecord的Map映射mTopFocusedAppByProcess.clear();// Go through the children in z-order starting at the top-most// 遍历DisplayContentfor (int i = mChildren.size() - 1; i >= 0; --i) {final DisplayContent dc = mChildren.get(i);
@frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java                // 针对每个DisplayContent,进行焦点窗口更新changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows, topFocusedDisplayId);// 不要自动将焦点重新分配离开应该保持焦点的应用程序窗口。findFocusedWindow` 将始终抓取瞬态启动的应用程序,因为它位于“顶部”,会造成不匹配,所以早点出去。if (mCurrentFocus != null && mTransitionController.shouldKeepFocus(mCurrentFocus)// 这只是保持焦点,所以如果焦点应用程序已被显式更改(例如通过 setFocusedTask),请不要提前退出。&& mFocusedApp != null && mCurrentFocus.isDescendantOf(mFocusedApp) && mCurrentFocus.isVisible() && mCurrentFocus.isFocusable()) {return false; }     // 寻找焦点窗口                   WindowState newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId); (mWmService.mPerDisplayFocusEnabled || topFocusedDisplayId == INVALID_DISPLAY) ? findFocusedWindow() : null;// 遍历windowstate mFindFocusedWindow会在找到新的焦点窗口时填充 mTmpWindow。forAllWindows(mFindFocusedWindow, true /* traverseTopToBottom */);/***用于查找给定窗口的聚焦窗口的 lambda 函数. 如果找到聚焦窗口,则lambda返回true,否则返回 false。如果找到焦点窗口,它将被存储在mTmpWindow中。*/private final ToBooleanFunction<WindowState> mFindFocusedWindow = w -> {final ActivityRecord focusedApp = mFocusedApp;  // 当前处于前台的ActivityRecord
@frameworks/base/services/core/java/com/android/server/wm/WindowState.java                                        if (!w.canReceiveKeys()) { return false; } // 如果窗口无法接收key事件,则不能作为焦点窗口,返回falsecanReceiveKeys(false /* fromUserTouch */);// 在瞬态启动期间,瞬态隐藏窗口不可见,或在顶部,但保持可聚焦,因此可以接收密钥。if (mActivityRecord != null && mTransitionController.shouldKeepFocus(mActivityRecord)) {  return true; }  // 可见或处于addWindow()~relayout之间final boolean canReceiveKeys = isVisibleRequestedOrAdding() final ActivityRecord atoken = mActivityRecord;// Surface已经创建,说明relayout()已经执行return (mHasSurface  || (!mRelayoutCalled && mViewVisibility == View.VISIBLE)) // relayout()未执行,且客户端View可见状态 && isVisibleByPolicy() && !isParentWindowHidden() // 如果是子窗口,其副窗口的mHidden属性为false&& (atoken == null || atoken.isVisibleRequested()) // mActivityRecord为null,或者其mVisibleRequested为true&& !mAnimatingExit && !mDestroying;// 没有执行退出动画   //且Surface没有进行销毁                                           && (mViewVisibility == View.VISIBLE)  // 客户端View可见 && !mRemoveOnExit  // 退出动画执行完毕&& ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)  // 没有FLAG_NOT_FOCUSABLE标记&& (mActivityRecord == null || mActivityRecord.windowsAreFocusable(fromUserTouch))  // mActivityRecord为null或者其可获焦&& (mActivityRecord == null || mActivityRecord.getTask() == null || !mActivityRecord.getTask().getRootTask().shouldIgnoreInput());//可以接受touch事件//是否支持靠背功能 是否是固定窗口下 且不是Display上的焦点根任务if(mAtmService.mHasLeanbackFeature && inPinnedWindowingMode() && !isFocusedRootTaskOnDisplay()) { return true }  // 防止画中画根任务接收电视上的输入// 除非用户故意触摸显示器,否则不允许不受信任的虚拟显示器接收密钥。return fromUserTouch || getDisplayContent().isOnTop() || getDisplayContent().isTrusted();if (focusedApp == null) { mTmpWindow = w;  return true; }// 如果前台没有Activity,则此次WindowState将作为焦点窗口返回if (!focusedApp.windowsAreFocusable()) { mTmpWindow = w; return true; } // 如果前台Activity是不可获焦的,则此次WindowState将作为焦点窗口返回// 如果当前WindowState由ActivityRecord管理,且非StartingWindow,则当前台Activity在当前WindowState所属Activity之上时,不存在焦点窗口if (activity != null && w.mAttrs.type != TYPE_APPLICATION_STARTING) { if (focusedApp.compareTo(activity) > 0) { mTmpWindow = null; return true; TaskFragment parent = activity.getTaskFragment();                                            if (parent != null && parent.isEmbedded()) {// 如果Activity当前嵌入到焦点任务中,则除非 Activity 与 FocusedApp 位于同一 TaskFragment 上,否则无法获得焦点。if (activity.getTask() == focusedApp.getTask() && activity.getTaskFragment() != focusedApp.getTaskFragment()) {  return false; } }}// 不满足以上条件,则此次WindowState将作为焦点窗口返回mTmpWindow = w;final WindowState imWindow = mInputMethodWindow;if (imWindow != null) {final WindowState prevTarget = mImeLayeringTarget;  // 更新IME target窗口final WindowState newTarget = computeImeTarget(true /* updateImeTarget*/); //获取新的IME Target窗口if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {                                assignWindowLayers(false /* setLayoutNeeded */); } //进行window layer的分配// IME target窗口发生变化,重新获取一次焦点窗口if (imWindowChanged) {mWmService.mWindowsChanged = true;setLayoutNeeded();newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId); }final WindowState oldFocus = mCurrentFocus;  //记录旧焦点窗口 mCurrentFocus = newFocus; // 更新新焦点窗口if (newFocus != null) {// 这两个列表用于记录ANR状态,表示自从焦点窗口为空时添加和移除的窗口mWinAddedSinceNullFocus.clear();mWinRemovedSinceNullFocus.clear();if (newFocus.canReceiveKeys()) { newFocus.mToken.paused = false; }  // 设置焦点窗口所在mToken.paused属性为false// IME target窗口发生变化,且旧焦点窗口非输入法窗口时if (imWindowChanged && oldFocus != mInputMethodWindow) {// Focus of the input method window changed. Perform layout if needed.if (mode == UPDATE_FOCUS_PLACING_SURFACES) {performLayout(true /*initial*/,  updateInputWindows);} else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {// Client will do the layout, but we need to assign layers for handleNewWindowLocked() below.assignWindowLayers(false /* setLayoutNeeded */); }// 向InputMonitor中设置焦点窗口if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {// If we defer assigning layers, then the caller is responsible for doing this part.getInputMonitor().setInputFocusLw(newFocus, updateInputWindows);}// 为IME窗口进行调整adjustForImeIfNeeded();updateKeepClearAreas();// 更新全局焦点窗口final WindowState newFocus = dc.mCurrentFocus;if (newFocus != null) {mTopFocusedAppByProcess.put(pidOfNewFocus, newFocus.mActivityRecord);topFocusedDisplayId = dc.getDisplayId();   }// 更新mTopFocusedDisplayId,表示焦点屏幕mTopFocusedDisplayId = topFocusedDisplayId;mWmService.mInputManager.setFocusedDisplay(topFocusedDisplayId);mWmService.mPolicy.setTopFocusedDisplay(topFocusedDisplayId);mWmService.mAccessibilityController.setFocusedDisplay(topFocusedDisplayId);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/863344.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MIX OTP——监督树和应用

在上一章关于 GenServer 的内容中&#xff0c;我们实现了 KV.Registry 来管理存储容器。在某个时候&#xff0c;我们开始监控存储容器&#xff0c;这样每当 KV.Bucket 崩溃时&#xff0c;我们就能采取行动。虽然变化相对较小&#xff0c;但它提出了一个 Elixir 开发人员经常问的…

独家原创 | Matlab实现CNN-Transformer多变量时间序列预测

SCI一区级 | Matlab实现BO-Transformer-GRU多变量时间序列预测 目录 SCI一区级 | Matlab实现BO-Transformer-GRU多变量时间序列预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现CNN-Transformer多变量时间序列预测&#xff1b; 2.运行环境为Matlab2023b…

【JavaScript】JavaScript简介

希望文章能给到你启发和灵感&#xff5e; 如果觉得文章对你有帮助的话&#xff0c;点赞 关注 收藏 支持一下博主吧&#xff5e; 阅读指南 JavaScript入门&#xff08;1&#xff09;————JavaScript简介开篇说明一、什么是JavaScript二、JavaScript的使用2.1 开发工具的选择…

fiddler抓包工具

概念 概念&#xff1a; Fiddler是一个http协议调试代理工具&#xff0c;它能够记录并检查所有你的电脑和互联网之间的http通讯。 http&#xff1a;不加密&#xff0c;端口为80 https&#xff1a;加密&#xff0c;端口为443 原理&#xff1a; 其实就在访问服务器时&#xff0…

如何在写代码中找到乐趣

平时我们写代码呢&#xff0c;多数情况都是流水线式写代码&#xff0c;基本就可以实现业务逻辑了。 如何在写代码中找到乐趣呢&#xff0c;我觉得&#xff0c;最好的方式就是&#xff1a;使用设计模式优化自己的业务代码。 参考资料&#xff1a; 实战&#xff01;工作中常用到…

[方法] Unity 3D模型与骨骼动画

1. 在软件中导出3D模型 1.1 3dsmax 2014 1.1.1 TGA转PNG 3dsmax的贴图格式为tga&#xff0c;我们需要在在线格式转换中将其转换为Unity可识别的png格式。 1.1.2 模型导出 导出文件格式为fbx。在导出设置中&#xff0c;要勾选三角算法&#xff0c;取消勾选摄像机和灯光&#…

三秒4张图!让 Stable Diffusion 出图速度暴增的新一代生成模型LCM!

前言 大家好&#xff0c;这里是和你们一起探索 AI绘画月月~ 最近一种新的图像生成形式逐渐兴起&#xff0c;即生成的图像会随输入的文字或笔画动作迅速变化&#xff0c;这让图像生成有了更多灵活探索和准确控制的空间。这种「实时反馈」的感觉源于模型能在几秒钟内&#xff0…

fiddler 返回Raw乱码

有时会发现自己发送的请求后&#xff0c;返回结果Raw里面是乱码&#xff0c;可以勾选Decode并重新发送请求就解决了 这个时候将Decode勾选一下 此时就好了

【C++ | 委托构造函数】委托构造函数 详解 及 例子源码

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

模版总结小全

BFS 最短步数问题 #include<iostream> #include<queue> #include<cstring> using namespace std;const int N 50; char g[N][N],d[N][N]; int dx[] {-1,0,1,0}; int dy[] {0,1,0,-1}; int n,m;int bfs(int x,int y){queue<pair<int,int> > q…

MySQL高级-SQL优化-insert优化-批量插入-手动提交事务-主键顺序插入

文章目录 1、批量插入1.1、大批量插入数据1.2、启动Linux中的mysql服务1.3、客户端连接到mysql数据库&#xff0c;加上参数 --local-infile1.4、查询当前会话中 local_infile 系统变量的值。1.5、开启从本地文件加载数据到服务器的功能1.6、创建表 tb_user 结构1.7、上传文件到…

mysql_config 命令, 可以查看mysqlclient库的位置在/usr/lib64/mysql下

好吧&#xff0c;其实我是从这里知道了 -l 后面加的库名和so文件这种名不一样&#xff0c;因为库文件实际叫下面这个名&#xff08;前面有lib)。

MySQL之覆盖索引

什么是覆盖索引&#xff1f; 覆盖索引&#xff1a;查询时使用了索引&#xff0c;且需要返回的列&#xff0c;在改索引中已经全部能找到。 示例&#xff1a;有user表如下&#xff1a; CREATE TABLE user (id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 技术主键,name varch…

Git企业开发---初识Git

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 引言 不知道大家有没有经历这种困惑&#xff0c;当我们要去交某文档时&#xff0c;总是要进行修改&#xff0c;修改后再交…

Softmax函数的作用

Softmax 函数主要用于多类别分类问题&#xff0c;它将输入的数值转换为概率分布。 具体来说&#xff0c;对于给定的输入向量 x [x_1, x_2,..., x_n] &#xff0c;Softmax 函数的输出为 y [y_1, y_2,..., y_n] &#xff0c;其中&#xff1a; 这样&#xff0c;Softmax 函数的输…

人生最有力,最棒的十句话!

人生最有力&#xff0c;最棒的十句话 1、允许一切事发生&#xff0c;所有一切发生的事不是你能阻挡了的&#xff0c;你接受&#xff0c;他也发生&#xff0c;你不接受&#xff0c;他也发生&#xff0c;你还不如坦然面对接受现实。 2、你焦虑的时候千万不要躺着啥也不干&#xf…

全网唯一免费无水印AI视频工具!

最近Morph Studio开始免费公测&#xff01;支持高清画质&#xff0c;可以上传语音&#xff0c;同步口型&#xff0c;最重要的是生成的视频没有水印&#xff01; Morph Studio国内就可以访问&#xff0c;可以使用国内邮箱注册&#xff08;我用的163邮箱&#xff09;&#xff0c;…

Java--回顾方法的调用

1.静态方法与非静态方法 1.当二者皆为静态方式时&#xff0c;可直接类名.方法名调用其方法 2.当调用的方法是静态&#xff0c;被调用的方法为非静态时&#xff0c;调用将会报错 3.出现2情况可通过进行实例化这个类的方式进行调用&#xff0c;如图所示 4.当处于一个类下&#xf…

在IDEA中创建Maven项目

2023版IDEA创建Maven项目&#xff08;新版&#xff09; 1.打开IDEA&#xff0c;点击 文件 -> 新建 -> 项目 2.创建Maven项目 3.编写java文件并运行 在src -> java -> 创建一个java文件并运行 如果出现下图 解决办法&#xff1a; 2022版IDEA创建Maven项目&#xf…

判断时间序列中的元素是否为:年初、年末、季初、季末

【小白从小学Python、C、Java】 【考研初试复试毕业设计】 【Python基础AI数据分析】 判断时间序列中的元素是否为&#xff1a; 年初、年末、季初、季末 Series.dt.is_year_start Series.dt.is_year_end Series.dt.is_quarter_start Series.dt.is_quarter_end 选择题 关于以下…