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 开发工具的选择…

JAVA 面试常见问题详解

JAVA 面试常见问题详解 目录 基础知识 Java基础面向对象编程异常处理 高级特性 多线程与并发集合框架JVM原理 实战问题 代码优化设计模式Spring框架 常见算法与数据结构 排序算法树与图动态规划 系统设计与架构 高并发系统设计分布式系统微服务架构 基础知识 Java基础 JDK…

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…

springMVC中的拦截器

文章目录 11、拦截器11.1书写拦截器的步骤一、首先需要在SpringMVC.xml中配置拦截器二、处理完配置文件进行配置类的书写书写的类需要实现HandlerInterceptor接口 11.2多个拦截器的执行顺序⑴如果每个拦截器的preHandle()都返回true⑵如果某个拦截器的preHandle()返回了false⑵…

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…

解释一下MVP和MVVM设计模式及其优缺点

MVP&#xff08;Model-View-Presenter&#xff09;设计模式 定义&#xff1a; MVP是一种软件架构设计模式&#xff0c;它将应用程序分为三个主要部分&#xff1a;模型&#xff08;Model&#xff09;、视图&#xff08;View&#xff09;和展示器&#xff08;Presenter&#xff…

模版总结小全

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…

【Ubuntu】如何用指令设置静态IP

这里介绍的是利用netplan 的配置文件一般在 /etc/netplan/ 目录下&#xff0c;文件名类似 01-network-manager-all.yaml。 用 nano/ vim 编辑器打开配置文件&#xff1a; sudo nano /etc/netplan/01-network-manager-all.yaml # 替换成你的文件名修改配置文件 network:versi…

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)。

策略模式、工厂模式和模板模式的应用

1、策略模式、工厂模式解决if else Cal package com.example.dyc.cal;import org.springframework.beans.factory.InitializingBean;public interface Cal extends InitializingBean {public Integer cal(Integer a, Integer b); }Cal工厂 package com.example.dyc.cal;impo…

一分钟彻底掌握Java迭代器Iterator

Iterator Iterator 是 Java 的 java.util 包中的一个接口 iterator() 是 Java 集合框架中的一个方法&#xff0c;它返回一个 Iterator 对象&#xff0c;该对象可以用来遍历集合中的元素。 Iterator确实是一个接口&#xff0c;你不能直接实例化一个接口。但是&#xff0c;你可以…

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;修改后再交…