Android窗口系统第三篇---WindowManagerService中窗口的组织方式

本文主要是介绍Android窗口系统第三篇---WindowManagerService中窗口的组织方式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Android窗口系统第一篇—Window的类型与Z-Order确定
Android窗口系统第二篇—Window的添加过程

上面文章梳理了一个窗口的添加过程,系统中有很多应用,每个应用有多个Activity,一个Activity上有一个Window,WindowManagerService是怎么管理的?先adb shell dumpsys activity查看一下Activity.
image.png

Display #0 (activities from top to bottom):
Display对应窗口系统中的DisplayContent类,可以理解成一个屏幕,手机上一般就是一块屏幕,当然也可以有虚拟屏幕,最近中兴推出了一款双屏幕的手机,那么这款手机上DisplayContent的size就为2,#0代表当前屏幕的设备ID。

Stack #0:

Stack对应WindwoManagerService中的TaskStack类,如果是在ActivityManagerService就对应ActivityStack类,为什么要引入TaskStack和ActivityStack类呢?因为他们的作用是管理TASK,一个Stack中包含了多个Task。应用程序也可以在AndroidManifest.xml文件中通过android:launchMode
指定当前Activity运行在哪一个Task中。#0代表Stack的id,android 中规定了id为0的Stack是存放home桌面的窗口的。在Android N以前,是不支持多窗口的,所以只有两个Stack,一个是HOME Stack,也就是存在桌面的Stack,另外一个就是存在其他应用程序的Stack。在android N 以后,总共有5个Stack。这些Stack都是在ActivityManager.java中定义的。


/** First static stack ID. */
public static final int FIRST_STATIC_STACK_ID = 0;/** Home activity stack ID. */
public static final int HOME_STACK_ID = FIRST_STATIC_STACK_ID;/** ID of stack where fullscreen activities are normally launched into. */
public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;/** ID of stack where freeform/resized activities are normally launched into. */
public static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1;/** ID of stack that occupies a dedicated region of the screen. */
public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;/** ID of stack that always on top (always visible) when it exist. */
public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;

Id等于0:Home Stack,就是Launcher所在的Stack。但是还有一些系统界面也运行在这个Stack上,比如近期任务的界面。
Id等于1:FullScren Stack,全屏的Activity所在的Stack。 但其实在分屏模式下,Id为1的Stack只占了半个屏幕。
Id等于2:Freeform模式的Activity所在Stack
Id等于3:Docked Stack 在分屏模式下,屏幕有一半运行了一个固定的应用,这就是Docked Stack
Id等于4:Pinned Stack 这是画中画Activity所在的Stack

  Stack #0:Task id #142TaskRecord{a11ed77 #142 A=com.miui.home U=0 sz=1}Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10800000 cmp=com.miui.home/.launcher.Launcher }Hist #0: ActivityRecord{2661ad9 u0 com.miui.home/.launcher.Launcher t142}Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10800000 cmp=com.miui.home/.launcher.Launcher }ProcessRecord{7da5d1c 2606:com.miui.home/u0a16}

所以上面的dum就可以这样解释,在id等于0这个stack管理一个id等于142的TASK,TASK中存放一个ActivityRecord,这个ActivityRecord就是HOME。了解了上面,就可以看下面这张图。
image.png

WindwoManagerService中需要管理多个显示屏幕,这些显示屏对象被保存在mDisplayContents列表里面,每一个显示屏对象中有一个mStackBoxs列表,这个列表包含多个StackBox,StackBox被组织成二叉树的形式,mFirst指向第一个节点的TaskStack对象,mSecond指向第二个节点的TaskStack对象,mStack指向当前TaskStack对象,一个TaskStack对象内部有mTask列表,这个列表中存储的对象是TASK,一个TASK中有很多Activity,一个Activity对应一个AppWindowToken。一个AppWindowToken中有一个windows列表,所以一个AppWindowToken对应多个WindowState。

DisPlayContent、TaskStack都解释过,现在看看什么是AppWindowToken?

说AppWindowToken需要先说WindowToken,WindowToken的子类AppWindowToken,你可以把WindowToken理解成是一个显示令牌,无论是系统窗口还是应用窗口,添加新的窗口的时候必须使用这个令牌向WMS表明自己的身份,添加窗口的时候会创建WindowToken,销毁窗口的时候移除WindowToken(removeWindowToken方法)。

WMS使用WindowToken将同一个应用组件(Activity,InputMethod,Wallpaper,Dream)的窗口组织在一起,换句话说,每一个窗口都会对应一个WindowToken,并且这个窗口中的所有子窗口将会对应同一个WindowToken,就如下面这个图的关系,假设窗口A是播放器中的一个窗口,除了主窗口外,还有三个子窗口,这些窗口的WindowToken都是一样的。

WindowToken和WindowState是1对多的关系.png

所以说WindowToken就像一个“户口本”,表示一家人都要在一个本本上。

AppWindowToken:每个App的Activity对应一个AppWindowToken。其中的appToken为IApplicationToken类型,连接着对应的AMS中的ActivityRecord::Token对象,有了它就可以顺着AppWindowToken找到AMS中相应的ActivityRecord。

这里为什么要针对Activity弄出一下AppWindowToken呢?我觉得有以下几个原因:
1、AppWindowToken是AMS中ActivityRecord::Token的产物,也就是ActivityRecord::Token可以看作ActivityRecord的一个远端句柄,在WMS中以AppWindowToken形式存在,ActivityRecord::Token实现了IApplicationToken。当WMS要通知AMS窗口变化时,就是用的这个接口。

2、一次连续性的操作(Task)可以打开多个Activity,每个Activity可以包含多个窗口(对应WindowState),WMS中TaskStack维护了Task的列表mTask,Task维护了AppWindowToken列表mAppTokens,AppWindowToken维护了相关WindowState的列表windows。即AppWindowToken被TaskStack管理,TaskStack中的mTasks是按历史顺序存放的,最老的Task在最底下,这个也是与AMS中ActivityStack的mTaskHistory顺序保持一致。如果没有AppWindowToken,直接用WindowToken,那么AMS的负担被增加,因为这些非Activity窗口,AMS是不care的。

AppWindowToken是什么时候被创建的呢?

在Activity窗口添加之前,AMS会调用addConfigOverride向WMS登记。

    void addConfigOverride(ActivityRecord r, TaskRecord task) {final Rect bounds = task.updateOverrideConfigurationFromLaunchBounds();// TODO: VI deal with activitymWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,(r.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, r.info.configChanges,task.voiceSession != null, r.mLaunchTaskBehind, bounds, task.mOverrideConfig,task.mResizeMode, r.isAlwaysFocusable(), task.isHomeTask(),r.appInfo.targetSdkVersion, r.mRotationAnimationHint);r.taskConfigOverride = task.mOverrideConfig;}

参数r.appToken,是ActivityRecord:Token对象。r.task.taskId是这个Activity运行的Task的ID, mStackId是这个Actiivty的Task所在的Stack的ID。

    @Overridepublic void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int userId,int configChanges, boolean voiceInteraction, boolean launchTaskBehind,Rect taskBounds, Configuration config, int taskResizeMode, boolean alwaysFocusable,boolean homeTask, int targetSdkVersion, int rotationAnimationHint) {....synchronized(mWindowMap) {AppWindowToken atoken = findAppWindowToken(token.asBinder());if (atoken != null) {Slog.w(TAG_WM, "Attempted to add existing app token: " + token);return;}//创建AppWindowTokenatoken = new AppWindowToken(this, token, voiceInteraction);atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;atoken.appFullscreen = fullscreen;atoken.showForAllUsers = showForAllUsers;atoken.targetSdk = targetSdkVersion;atoken.requestedOrientation = requestedOrientation;atoken.layoutConfigChanges = (configChanges &(ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0;atoken.mLaunchTaskBehind = launchTaskBehind;atoken.mAlwaysFocusable = alwaysFocusable;if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken+ " to stack=" + stackId + " task=" + taskId + " at " + addPos);atoken.mRotationAnimationHint = rotationAnimationHint;//mTaskIdToTask是一个SparseArray对象,先尝试取出Task,没有就创建一个Task task = mTaskIdToTask.get(taskId);if (task == null) {task = createTaskLocked(taskId, stackId, userId, atoken, taskBounds, config);}//将atoken放到task中的addPos位置task.addAppToken(addPos, atoken, taskResizeMode, homeTask);//atoken放到mTokenMap中mTokenMap.put(token.asBinder(), atoken);// Application tokens start out hidden.atoken.hidden = true;atoken.hiddenRequested = true;}}
    private Task createTaskLocked(int taskId, int stackId, int userId, AppWindowToken atoken,Rect bounds, Configuration config) {if (DEBUG_STACK) Slog.i(TAG_WM, "createTaskLocked: taskId=" + taskId + " stackId=" + stackId+ " atoken=" + atoken + " bounds=" + bounds);//这里是能够取到的,因为AMS在调用addAppWindowToken之前,会调用WMS的attachStack方法,提前将TaskStack创建出来。final TaskStack stack = mStackIdToStack.get(stackId);if (stack == null) {throw new IllegalArgumentException("addAppToken: invalid stackId=" + stackId);}EventLog.writeEvent(EventLogTags.WM_TASK_CREATED, taskId, stackId);Task task = new Task(taskId, stack, userId, this, bounds, config);//把上面创建的Task放到mTaskIdToTask中mTaskIdToTask.put(taskId, task);//把task放到stack中,返回taskstack.addTask(task, !atoken.mLaunchTaskBehind /* toTop */, atoken.showForAllUsers);return task;}

现在从Activity的角度来看Activiy窗口的组织方式应该如下图。
image.png

TaskRecord和ActivityRecord:Token是AMS包下面的类,Task和DisplayContent是WMS包下面的类,其中TaskRecord和Task对应,TaskRecord中有mActivities列表,这个列表中放了若干ActivityRecord,每一个ActivityRecord都有一个Token,这个Token要通过WMS的addAppToken给加入到WMS中的Task中去,AMS和WMS都是运行在system_server进程下面,所有addAppToken并不是一个IPC过程。每一个AppWindwToken可以对应一个WindowState或者对应多个WindowState,图中的WindowState-Z是主窗口,WindowState-Z-1是子窗口,每一个WindowState是属于一个屏幕(DisplayContent)中。另外不仅App的窗口添加的时候需要调用addAppToken注册Token,其他类型的窗口添加也需要向WMS注册token,比如墙纸类型的窗口在添加的时候,也需要提前调用addWindowToken向WMS中注册token,这样后面执行addWindow的过程才不会失败。

这篇关于Android窗口系统第三篇---WindowManagerService中窗口的组织方式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1130281

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

基于人工智能的图像分类系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 图像分类是计算机视觉中的一个重要任务,目标是自动识别图像中的对象类别。通过卷积神经网络(CNN)等深度学习技术,我们可以构建高效的图像分类系统,广泛应用于自动驾驶、医疗影像诊断、监控分析等领域。本文将介绍如何构建一个基于人工智能的图像分类系统,包括环境

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟 开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚 第一站:海量资源,应有尽有 走进“智听

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

【区块链 + 人才服务】可信教育区块链治理系统 | FISCO BCOS应用案例

伴随着区块链技术的不断完善,其在教育信息化中的应用也在持续发展。利用区块链数据共识、不可篡改的特性, 将与教育相关的数据要素在区块链上进行存证确权,在确保数据可信的前提下,促进教育的公平、透明、开放,为教育教学质量提升赋能,实现教育数据的安全共享、高等教育体系的智慧治理。 可信教育区块链治理系统的顶层治理架构由教育部、高校、企业、学生等多方角色共同参与建设、维护,支撑教育资源共享、教学质量评估、

软考系统规划与管理师考试证书含金量高吗?

2024年软考系统规划与管理师考试报名时间节点: 报名时间:2024年上半年软考将于3月中旬陆续开始报名 考试时间:上半年5月25日到28日,下半年11月9日到12日 分数线:所有科目成绩均须达到45分以上(包括45分)方可通过考试 成绩查询:可在“中国计算机技术职业资格网”上查询软考成绩 出成绩时间:预计在11月左右 证书领取时间:一般在考试成绩公布后3~4个月,各地领取时间有所不同