如何应对Android面试官 -> WindowManagerService 启动流程分析

前言


image.png

本章主要从上面几个角度来讲解 WindowManagerService;

相关概念


介绍 WMS 之前,我们先来介绍几个相关的概念;

WMS 存在于 system_server 系统服务进程,view 存在于 app 进程,所有的窗口最终都是通过 wms 来进行管理,为了方便管理,app 和 wms 之间添加了 WindowManager,用来在应用与 wms 之间的管理接口,管理窗口顺序,消息等。

Activity 与 Window 相关概念

  • Activity 只负责生命周期和事件处理;
  • Window 只控制视图;
  • 一个 Activity 包含一个 Window,如果 Activity 没有 Window,那就相当于 Service;
  • AMS 统一调度所有应用程序的 Activity;
  • WMS 控制所有 Window 的显示与隐藏以及要显示的位置;

Window

『Window』表明它是和窗口相关的,“窗口”是一个抽象的概念,从用户的角度来讲,它是一个“界面”;从SurfaceFlinger 的角度来看,它是一个 Layer,承载着和界面有关的数据和属性;从 WMS 角度来看,它是一个WindowState,用于管理和界面有关的状态;添加一个 Window 的过程,就是分配一块 Surface 的过程;

  • 表示一个窗口的概念,是所有 View 的直接管理者,任何视图都通过 Window 呈现(点击事件由 Window->DecorView->View; Activity 的 setContentView 底层通过 Window 完成);
  • Window是一个抽象类,具体且唯一的实现是 PhoneWindow;
  • 创建 Window 需要通过 WindowManager 创建;
  • WindowManager 是外界访问 Window 的入口;
  • Window 具体实现位于 WindowManagerService 中;
  • WindowManager 和 WindowManagerService 的交互是通过 IPC 完成;
  • 定义窗口样式和行为的抽象基类,用于作为顶层的 view 加到 WindowManager 中,其实现类是 PhoneWindow
  • 每个 Window 都需要指定一个 Type(应用窗口、子窗口、系统窗口)。Activity 对应的窗口是应用窗口;PopupWindow,ContextMenu,OptionMenu 是常用的子窗口;像 Toast 和系统警告提示框(如ANR)就是系窗口,还有很多应用的悬浮框也属于系统窗口类型;

image.png

Android上对 Window 进行了弱化,主要是 View;

WindowManager

用来在应用与window之间的管理接口,管理窗口顺序,消息等;

WindowManagerService

简称 WMS,WindowManagerService 管理窗口的创建、更新和删除、显示顺序等,是 WindowManager 这个管理接口的真正的实现类。它运行在 system_server 进程,作为服务端,客户端(应用程序)通过 IPC 调用和它进行交互;

  • Z-ordered的维护函数;
  • 输入法管理;
  • AddWindew/RemoveWindow;
  • Layout;
  • Token管理 AppToken;
  • 活动窗口管理(FocusWindow);
  • 活动应用管理(FocusApp);
  • 转场动画;
  • 系统消息收集线程;
  • 系统消息分发线程;

image.png

image.png

WMS、WindowManager、Window 三者之间的一个关系;Window 是 App 这边用来管理 View 的一个概念,它是一个抽象的概念,我们真正看到的是 View 而不是 Window,View 最终如果要显示出来,需要交给 WMS,WMS会把 View 交给 SurfaceFlinger,而 SurfaceFlinger 最终会交给 OpenGL 进行真正的绘制,绘制完成后显示到屏幕上;

image.png

Token

这里提到的 Token 主是指窗口令牌(WindowToken),是一种特殊的 Binder 令牌,wms 用它唯一标识系统中的一个窗口;

Window 的 type

窗口类型;

  • 应用窗口;
  • 子窗口;
  • 系统窗口;

mTokenMap

保存所有显示令牌「类型为WindowToken」一个窗口必须隶属于某一个显示令牌;

mAppTokens,保存了所有属于 Activity 的显示令牌( WindowToken 的子类 AppWindowToken),mAppTokens 列表是有序的,它与 AMS 中的 mHistory 列表的顺序保持一致,反映了系统中 Activity 的顺序;

mExitingTokens,保存了正在退出过程中的显示令牌等;

WindowToken 用来表示一组 Window;

mWindowMap

保存所有的状态信息「类型为WindowState」;

mPendingRemove,保存了那些退出动画播放完成并即将被移除的窗口;

mLosingFocus,保存了那些失去了输入焦点的窗口;

在 DisplayContent 中,也有一个 windows 列表,这个列表存储了显示在此 DisplayContent 中的窗口,并且它是有序的。窗口在这个列表中的位置决定了其最终显示时的Z序;

mSessions

保存了所有想向 WMS 寻求窗口管理服务的客户端。注意 session 具有进程唯一性;App 进程通过建立 Session代理对象和 Session 对象通信,进而和WMS建立连接;

WindowState

在 Client 端用 Window 来表示,这个 Window 到了 WMS 这边换了一种表示方式,就是 WindowState;类似 Activity 在 App 侧是 Activity,在 AMS 侧就是 ActivityRecord;

AppTransition

App 启动的时候窗口动画使用;

启动流程

WMS 的启动也是通过 SystemServer 的 run 调用的 startOtherService 方法中启动的;

private void startOtherServices(@NonNull TimingsTraceAndSlog t) {// 省略部分代码...wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
}

这里直接调用了 WindowManagerService 的静态 main 方法;

public static WindowManagerService main(final Context context, final InputManagerService im,final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,ActivityTaskManagerService atm, DisplayWindowSettingsProviderdisplayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory,Supplier<Surface> surfaceFactory,Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {DisplayThread.getHandler().runWithScissors(() ->sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,atm, displayWindowSettingsProvider, transactionFactory, surfaceFactory,surfaceControlFactory), 0);return sInstance;
}

这里要注意的是 WMS 的初始化在 DisplayThread 一个单独的线程中,并没有在 SystemServer 的主线程中;我们先来看下这个 DisplayhThread 是什么

public final class DisplayThread extends ServiceThread {}
public class ServiceThread extends HandlerThread {}

本质上就是一个 HandlerThread,HandlerThread 我们在前面的 Handler 中有涉及,这里主要是拿到 Handler 之后调用了其 runWithScissors 方法,我们来看下这个 runWithScissors 方法;

public final boolean runWithScissors(@NonNull Runnable r, long timeout) {if (r == null) {throw new IllegalArgumentException("runnable must not be null");}if (timeout < 0) {throw new IllegalArgumentException("timeout must be non-negative");}if (Looper.myLooper() == mLooper) {r.run();return true;}BlockingRunnable br = new BlockingRunnable(r);return br.postAndWait(this, timeout);
}

这里使用了一个 BlockingRunnable,也就是说这里进行了阻塞,在 handler.post 中,让这个被post的消息执行之后再继续往下执行;

DisplayThread.getHandler().runWithScissors(new Runnable() {@Overridepublic void run() {sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy, atm, displayWindowSettingsProvider, transactionFactory, surfaceFactory, surfaceControlFactory)}
}, 0);
return sInstance;

也就是说要等 WindowManagerService 创建完成之后才能继续往下执行,才能 return sInstance;

我们接着来看下 new WindowManagerService 做了什么

private WindowManagerService(Context context, InputManagerService inputManager,boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,ActivityTaskManagerService atm, DisplayWindowSettingsProviderdisplayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory,Supplier<Surface> surfaceFactory,Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {// 创建动画管家mAnimator = new WindowAnimator(this);// 获取 ActivityManagerServicemActivityManager = ActivityManager.getService();
}

初始化了一些需要与 WMS 通信的服务;WMS 创建完成之后,会和 AMS 进行一个绑定;

wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
// 进行绑定        
mActivityManagerService.setWindowManager(wm);

绑定之后,会调用 wms 的 ready 逻辑;

// 相关 ready
wm.onInitReady();
// 相关 ready
try {wm.displayReady();
} catch (Throwable e) {reportWtf("making display ready", e);
}// 相关 ready
try {wm.systemReady();
} catch (Throwable e) {reportWtf("making Window Manager Service ready", e);
}

总结一下,主要是下面两点:

  1. WMS 的启动主要涉及3个线程:system_server、android.display、android.ui;其中WMS.H.handleMessage 运行在 android.display 线程中;
  2. WMS 中3个关键步骤:创建 WMS 对象,初始化显示信息,处理 systemready 通知

窗口类型


应用窗口(Application Window)

包括所有应用程序自己创建的窗口,以及在应用起来之前系统负责显示的窗口,层级范围是1~99;

  • FIRST_APPLICATION_WINDOW=1;第一个应用窗口
  • TYPE_BASE_APPLICATION=1;所有程序窗口的base窗口,其他应用程序窗口都显示在它上面
  • TYPE_APPLICATION=2;所有Activity的窗口,只能配合Activity在当前APP使用
  • TYPE_APPLICATION_STARTING=3;目标应用窗口未启动之前的那个窗口
  • LAST_APPLICATION_WINDOW=99;最后一个应用窗口

子窗口

应用自定义的对话框,或者输入法窗口,子窗口必须依附于某个应用窗口(设置相同的token),层级范围是1000~1999;

  • FIRST_SUB_WINDOW=1000;第一个子窗口
  • TYPE_APPLICATION_PANEL=FIRST_SUB_WINDOW;面板窗口,显示于宿主窗口的上层,只能配合Activity在当前APP使用
  • TYPE_APPLICATION_MEDIA=FIRST_SUB_WINDOW+1;媒体窗口(例如视频),显示于宿主窗口下层
  • TYPE_APPLICATION_SUB_PANEL=FIRST_SUB_WINDOW+2;应用程序窗口的子面板,只能配合Activity在当前APP使用(PopupWindow默认就是这个Type)
  • TYPE_APPLICATION_ATTACHED_DIALOG=FIRST_SUB_WINDOW+3;对话框窗口,只能配合Activity在当前APP使用
  • TYPE_APPLICATION_MEDIA_OVERLAY=FIRST_SUB_WINDOW+4;TYPE_APPLICATION_MEDIA的重影窗口,显示TYPE_APPLICATION_MEDIA和应用窗口之间
  • LAST_SUB_WINDOW=1999;最后一个子窗口

系统窗口

系统设计,不依附于任何应用窗口,比如:状态栏(Status Bar)、导航栏(Navigation Bar)、壁纸(Wallpaper)、来电显示窗口(Phone)、锁屏窗口(KeyGuard)、信息提示窗口(Toast)、音量调整窗口、鼠标光标等等,层级范围是2000~2999;

  • FIRST_SYSTEM_WINDOW=2000;系统窗口,非应用程序创建
  • TYPE_STATUS_BAR;状态栏,只能有一个状态栏,位于屏幕顶端,其他窗口都位于它下方
  • TYPE_SEARCH_BAR;搜索栏,只能有一个搜索栏,位于屏幕上方
  • TYPE_PHONE;电话窗口,它用于电话交互(特别是呼入),置于所有应用程序之上,状态栏之下,属于悬浮窗(并且给一个Activity的话按下HOME键会出现看不到桌面上的图标异常情况)
  • TYPE_SYSTEM_ALERT;系统警告提示窗口,出现在应用程序窗口之上,属于悬浮窗, 但是会被禁止
  • TYPE_TOAST;信息窗口,用于显示Toast, 不属于悬浮窗, 但有悬浮窗的功能
  • TYPE_SYSTEM_OVERLAY;系统顶层窗口,显示在其他一切内容之上,此窗口不能获得输入焦点,否则影响锁屏
  • LAST_SYSTEM_WINDOW=2999;最后一个系统窗口

好了,今天的 WMS 就先讲解到这里吧

下一章预告


window 的显示次序,尺寸计算;

欢迎三连


来都来了,点个关注点个赞吧,你的支持是我最大的动力~

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

【算法与数据结构】数组

文章目录 前言数组数组的定义数组的基本操作增加元素删除元素修改元素查找元素 C STL 中的数组arrayvector Python3 中的列表访问更改元素值遍历列表检查列表中是否存在某元素增加元素删除元素拷贝列表总结 Python3 列表的常用操作 参考资料写在最后 前言 本系列专注更新基本数…

uniapp的app端推送功能,不使用unipush

1&#xff1a;推送功能使用htmlPlus实现&#xff1a;地址HTML5 API Reference (html5plus.org) 效果图&#xff1a; 代码实现&#xff1a; <template><view class"content"><view class"text-area"><button click"createMsg&q…

跨界内容营销:Kompas.ai如何帮助你的品牌打破行业边界

在当今多元化的市场环境中&#xff0c;跨界营销已成为品牌拓展影响力和用户基础的重要策略。通过跨界合作&#xff0c;品牌能够打破行业界限&#xff0c;创造独特的用户体验&#xff0c;从而提升品牌形象和市场竞争力。本文将深入分析跨界营销的作用&#xff0c;详细介绍Kompas…

AI-powered的搜索引擎:Perplexity 与知识工作者

Perplexity是一款AI-powered的搜索引擎&#xff0c;通过与OpenAI合作&#xff0c;利用GPT模型提供高速、准确的搜索结果&#xff0c;特别针对知识工作者的需求进行优化。 知识工作者通常需要进行复杂的研究和决策&#xff0c;他们希望能够快速获取准确的信息来支持他们的工作。…

buuctf-misc题目练习三

荷兰宽带数据泄露 BIN 文件&#xff0c;也称为二进制文件&#xff0c;是一种压缩文件格式&#xff0c;可以 包含图像和视频等信息 , 并被许多应用程序用于各种目的。 RouterPassView是一个找回路由器密码的工具。 大多数现代路由器允许备份到一个文件路由器的配置&#xff0c…

Unity TileMap入门

概述 相信很多同学学习制作游戏都是从2D游戏开始制作的吧&#xff0c;瓦片地图相信大家都有接触&#xff0c;那接下来让我们学习一下这部分的内容吧&#xff01; Tilemap AnimationFrameRate:设置每帧动画的播放速率。Color:瓦片地图的颜色TileAnchor:锚点&#xff0c;&#x…

100000订单直接拒掉,君子爱财,取之有道

近一个月询盘可谓寥寥无几&#xff0c;成交率为0&#xff0c;今天好不容易接了一个客户询盘&#xff0c;订单总价高达100000&#xff0c;听完细节直接拒掉&#xff0c;至于原因懂的都懂&#xff0c;不懂得等我慢慢道来。 前两天有2个询盘&#xff0c;其中一个是二次开发&#x…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-15.4讲 GPIO中断实验-IRQ中断服务函数详解

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

如何利用代理IP进行SEO优化?

“SEO”这个词相信对于做在线业务的朋友来说一定不陌生。 在网络营销中&#xff0c;SEO是至关重要的一环&#xff0c;对于增加有机流量、提升品牌知名度、增加网站的信任度和权威性非常有效。而代理IP在SEO优化中有着不可或缺的作用&#xff0c;它可以帮助网站管理员和SEO专家…

数据中心法

数据中心法是实现词法分析器的结构化方法。通过设计主表和子表分开存储状态转移信息&#xff0c;实现词法分析器的控制逻辑和数据结构分离。 主要解决了状态爆炸、难以维护和复杂性的问题。 状态爆炸是指当状态和转移较多时&#xff0c;单一使用一个表来存储所有的信息的话会导…

这3种深拷贝实现,你都知道吗?

目录&#xff1a; 1、JSON.parse 2、structuredClone 3、cloneDeep

Leetcode—138. 随机链表的复制【中等】(cend函数)

2024每日刷题&#xff08;129&#xff09; Leetcode—138. 随机链表的复制 实现代码 /* // Definition for a Node. class Node { public:int val;Node* next;Node* random;Node(int _val) {val _val;next NULL;random NULL;} }; */class Solution { public:Node* copyRan…

【vivado】debug相关时钟及其约束关系

一、前言 在xilinx fpga的degug过程中&#xff0c;经常出现由于时钟不对而导致的观测波形失败&#xff0c;要想能够解决这些问题需要了解其debug的组成环境以及之间的数据流。本文主要介绍debug过程中需要的时钟及各时钟之间的关系。 二、debug相关时钟 Vivado 硬件管理器使…

【CCF-CSP】202403-3 化学方程式配平

输入格式&#xff1a; 从标准输入读入数据。 输入的第一行包含一个正整数 n&#xff0c;表示需要判断的化学方程式的个数。 接下来的 n 行&#xff0c;每行描述了一个需要被配平的化学方程式。包含空格分隔的一个正整数和全部涉及物质的化学式。其中&#xff0c;正整数 m 表…

KMeans,KNN,Mean-shift算法的学习

1.KMeans算法是什么&#xff1f; 在没有标准标签的情况下&#xff0c;以空间的k个节点为中心进行聚类&#xff0c;对最靠近他们的对象进行归类。 2.KMeans公式&#xff1a; 2. 1.关键分为三个部分&#xff1a; 1.一开始会定义n个中心点&#xff0c;然后计算各数据点与中心点…

企业活动想联系媒体报道宣传如何联系媒体?

在企业的宣传推广工作中,我曾经历过一段费事费力、效率极低的时期。那时,每当公司有重要活动或新项目需要媒体报道时,我便要一家家地联系媒体,发送邮件、打电话,甚至亲自登门拜访,只为求得一篇报道。然而,这样的过程充满了不确定性和挑战,时常让我感到焦虑和压力山大。 记得有一…

在CentOS 7服务器及Windows 10客户端间建立并配置NFS服务

在CentOS 7服务器及Windows 10客户端间建立并配置NFS服务 引言 网络文件系统(Network File System)&#xff0c;简称NFS&#xff0c;是一种分布式文件系统协议。它允许网络上的客户端机器像访问本地磁盘文件一样&#xff0c;通过网络访问服务器上的文件。在某些特定的业务场景中…

鸿蒙内核源码分析(文件句柄篇) | 你为什么叫句柄

句柄 | handle int open(const char* pathname,int flags); ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count); int close(int fd);只要写过应用程序代码操作过文件不会陌生这几个函数,文件操作的几个关键步骤嘛,跟把大…

202466读书笔记|《一天一首古诗词》——借问梅花何处落,风吹一夜满关山

202466读书笔记|《一天一首古诗词》——借问梅花何处落&#xff0c;风吹一夜满关山 上册下册 《一天一首古诗词》作者李锡琴&#xff0c;蛮早前加入书架的已购买书籍&#xff0c;这次刚好有点时间&#xff0c;利用起来读完。 赏析没有细看&#xff0c;只读了诗词部分&#xff0…