区块链技术博客
www.b2bchain.cn

Android Detail:站在 Window 视角理解 Activity 任务与返回栈求职学习资料

本文介绍了Android Detail:站在 Window 视角理解 Activity 任务与返回栈求职学习资料,有助于帮助完成毕业设计以及求职,是一篇很好的资料。

对技术面试,学习经验等有一些体会,在此分享。

前言

很高兴见到你!👋

前面 我们讨论了 Activity 任务与返回栈。我在标题上使用了「老生常谈」这个词,是因为文中虽然使用了很多动画来展示 Activity 任务与返回栈的概念模型,但与网上的众多文章一样,仍旧是站在 Activity 的角度来讨论的(我 diss 我自己 🤪)。

不知你是否思考过以下问题:

  • 所有人都知道 Activity 可以展示 UI 界面,但 App 一定要有 Activity 吗?换句话说没有 Activity 就不能展示界面吗?
  • 状态栏和导航栏究竟是什么?
  • Activity 所谓的在前台,获得焦点,对用户可见等描述究竟是什么意思?
  • Android 中所谓的窗口是什么?
  • Android 中多窗口是如何实现的?
  • Android 中可以像桌面操作系统一样随意控制窗口的大小吗?

本文将以另一种视角来讨论 Activity 任务与返回栈,会涉及一些源码逻辑,源码部分主要基于 Android 10 与 Android 11。

涉及源码的部分已在标题中标记,如果觉得源码过多影响阅读体验,可以先跳过带有源码的部分,以理解概念为主,将主要概念理解后再回头查看。😉

前置知识

  • Android Detail:官方文档不是圣经,老生常谈的 Activity 任务,返回栈

  • 重学安卓:Activity 的快乐你不懂!

👆 强烈建议阅读,而且要多读几遍!

  • ActivityManagerService 与 WindowManagerService 简单了解

文章目录一览

  • 前言
  • 前置知识
  • 文章目录一览
  • App 一定要有 Activity 吗?
  • 状态栏与导航栏
    • 状态栏与导航栏是通过 Window 直接管理的
    • 状态栏与导航栏的显示与隐藏
  • 常见的 Window
  • 为何要用 Activity 和 Window 两个视角解读?
  • 多任务与多窗口
    • 开启自由窗口
    • WindowMode
  • AMS 与 WMS 对应的概念
    • ActivityType 与 WindowType
    • Android 11 前的栈管理

App 一定要有 Activity 吗?

不一定。其实这个问题很简单,因为存在一些没有界面的 app,例如源码中的 SettingsProvider,它仅有一个 Provider。

SettingsProvider 在设备的这个 👇 位置,感兴趣的小伙伴可以反编译一下,的确只有一个 Provider。

/system/priv-app/SettingsProvider/SettingsProvider.apk

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

SettingsProvider 位置

前面提到的是没有界面的 app ,那如果一个有 UI 界面的 app 一定要有 Activity 吗?

思考 ing ~ 🤔💭


答案也是不一定。

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

没有 Activity 的带有界面的 app

上图是一个只有一个 Service 的 app,通过 adb 命令启动 Service 后显示了一个全屏界面。

操作步骤:

  1. 安装根目录 window.apk
  2. 为该 app 开启悬浮窗权限
  3. adb 执行该命令 adb shell am startservice -n flywith24.android.detail.window/com.flywith24.detail.MainService

app 地址

有经验的小伙伴已经猜到,这个所谓的界面就是一个悬浮窗。

现在出现了一个问题:Android 中 Activity 不是显示 UI 的必要条件,是什么呢?

重学安卓:Activity 的快乐你不懂! 中早给出了答案:Window

在 Android 中,暴露给开发者操作 UI 界面的 API 是 mWindowManager.addView(rootView, windowParams);

实际上绕过 Window 还可以有更底层的实现方式,这里就不深入展开了。

状态栏与导航栏

如果觉得上一节的切入点比较抽象的话,我们以一个更加常用的入口来切入——状态栏与导航栏。

Android 中(特别是手机,TV 等特殊设备可能不需要),状态栏和导航栏是十分重要的组件。

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

System bars

我们通过 adb shell dumpsys window displays 命令输出当前的 display 情况,找到 WindowInsetsStateController 关键字,可以看到 顶部状态栏底部导航栏 所占的区域(基于 Android 11):

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

(0,0)(1080,66) 所占的区域为顶部状态栏;

(0,2028)(1080,2160) 所占的区域为底部导航栏;

一般来说,剩余部分是应用程序显示的区域,当然应用程序可以隐藏状态栏与导航栏以实现全屏显示的效果。

状态栏与导航栏是通过 Window 直接管理的

状态栏与导航栏是由 SystemUI 来进行管理的。

SystemUI 是一个系统级 app,为用户提供系统级别的信息显示与交互的一套 UI 组件,除了状态栏与导航栏,它还管理着壁纸,截图,最近任务列表(9.0 后在 Launcher 中)等组件。

而本质上,状态栏和导航栏都是通过 Window 直接管理的

源码中状态栏和导航栏的 View 与控制器分别为:

  • com.android.systemui.statusbar.phone.StatusBarWindowView

  • com.android.systemui.statusbar.phone.NavigationBarView

  • com.android.systemui.statusbar.phone.StatusBarWindowController

  • com.android.systemui.statusbar.NavigationBarController

其中 StatusBarWindowController 的 attach() 方法将 StatusBarWindowView 添加至 WindowManager

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

导航栏的内容稍显复杂(导航栏不仅显示三大金刚键,旋转时导航栏有旋转按钮,键盘弹出时有键盘按钮),它的添加逻辑被封装在 com.android.systemui.statusbar.phone.NavigationBarFragment 中,NavigationBarFragment 中的 create() 方法将 NavigationBarView 添加到 WindowManager。最终使用 fragment 的 onCreateView 加载布局。

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

状态栏与导航栏的显示与隐藏

官方为开发者提供了一套管理状态栏和导航栏的 API,通过 View 中的 setSystemUiVisibility() 方法即可管理。

但该 API 在 Android 11 中弃用了,取而代之的是使用 WindowInsetsController 统一管理。

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

如何显示/隐藏状态栏与导航栏

下图演示了显示/隐藏 状态栏,导航栏,软键盘 的操作:

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

管理状态栏、导航栏、软键盘

常见的 Window

开发中我们经常接触到的 Window 有 PopupWindow ,Dialog,Toast 自定义的悬浮窗等。

PopupWindow 中的 invokePopup() 方法内部调用了 mWindowManager.addView(decorView, p);

dialog 的 show() 方法中调用了 mWindowManager.addView(mDecor, l);

显示 Toast 的逻辑稍微复杂些,最终会执行 ToastPresenter#show() 方法,里面调用了 mWindowManager.addView(mView, mParams);

为何要用 Activity 和 Window 两个视角解读?

这里要引出两个系统服务:

  • ActivityManagerService,后文简称 AMS
  • WindowManagerService,后文简称 WMS

以上两个服务运行在 system_server 进程中。

这部分可以理解为一个 C/S 模型,App 为 客户端,system_server 为服务端。App 端调用的很多 API 都是发送到 system_server 进程中的系统服务中处理的。此处不详细展开。

AMS 虽然名字中有着 Activity,但其负责了 Android 中四大组件的管理。之前 我们介绍了 dumpsys 的原理,其中 adb shell dumpsys activity 输出的便是 AMS 的信息。

我们可以使用以下命令来输出四大组件的详细信息:

  • dumpsys activity activities
  • dumpsys activity broadcasts
  • dumpsys activity services
  • dumpsys activity providers

WMS 主要负责 Window 的管理,它的主要职责有:

  • 启动/隐藏窗口
  • 窗口动画
  • 窗口大小
  • 窗口层级
  • 事件派发

从 AMS 的视角只能分析任务与返回栈的逻辑概念,但应用程序都是以可视化的界面展示的,所以我们还需从 WMS 的角度再次解读这部分内容。

这更像是对已知存在事物 「如何实现」 的推演,至于对「为何存在如此设计」的追溯,重学安卓:Activity 的快乐你不懂! 中已详细介绍。

这也正是两个专栏存在的意义,本专栏 专注于前者,重学安卓 专注于后者。

此处只是引入这两个概念,视图系统系列会对这部分进行详细介绍。

所谓的 Activity 与 Window 视角便是 AMS 与 WMS 视角,后文以此描述。

多任务与多窗口

Android Detail:官方文档不是圣经,老生常谈的 Activity 任务,返回栈 中我们强调了 Android 是基于多任务理念设计的,而多任务更像是一种抽象的逻辑概念。在桌面操作系统中,一个屏幕中显示多个窗口是十分常见的使用场景。而在移动操作系统中,受限于移动设备屏幕尺寸,Android 在 7.0 才开始支持分屏。多窗口是一个相对比较好理解的概念,因为它有着可视化的表现方式。

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

Android 中的分屏

多任务是逻辑上的概念,多窗口是视觉上的概念。

Android 中的多窗口分为四种模式:

  • 全屏

  • 分屏(7.0 引入)

    Android Detail:站在 Window 视角理解 Activity 任务与返回栈

    分屏模式

  • 画中画(8.0 引入)

    Android Detail:站在 Window 视角理解 Activity 任务与返回栈

    画中画模式

  • FreeForm (自由窗口)

    Android Detail:站在 Window 视角理解 Activity 任务与返回栈

    自由窗口模式

分屏和画中画开启方式比较常见,下面主要介绍如何开启自由窗口。

开启自由窗口

开启自由窗口模式,需要执行以下命令:

adb shell settings put global enable_freeform_support 1

然后重启手机

在高版本上可以通过开发者选项来开启

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

在高版本设备通过开发者选开启自由窗口

接着,在 最近任务 界面长按应用图标便出现了自由窗口的选项。

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

自由窗口选项

WindowMode

为了区别不同的窗口模式,Android 中引入了 WindowMode 的概念:

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

最常见的模式为全屏,分屏状态下的两个窗口模式分别为 「分屏-主」,「分屏-次」,自由窗口也有其特殊的窗口模式。

AMS 与 WMS 对应的概念

ActivityType 与 WindowType

为了方便区分 Activity 和 Window 的类型,Android 引入了 ActivityType 与 WindowType 的概念,下表展示了主要的类型值及其含义:

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

最常见的 Activity 类型是标准类型,Launcher 有着独特的 Home 类型。

当栈顶的 Activity 类型为 Home 时,即使栈中有其他 Task,点击返回键也不能切换,即在桌面点击返回键不会退出应用显示栈中的下一个应用。这也是设计 ActivityType 的主要原因

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

设计 WindowType 是为了配置窗口的显示顺序考虑的。

Android 中主要有三种窗口类型:

  • App Window:取值范围为 1—99
  • Sub Window:取值范围为 1000—1999
  • System Window:取值范围为 2000—2999

众所周知,Android 中以屏幕左上角为原点,向右为 X 轴的正方向,向下为 Y 轴的正方向建立了一个二维空间。为了方便管理窗口的显示次序,屏幕被扩展为一个三维空间,定义了一个 Z 轴,方向垂直于屏幕表面指向屏幕外。多个窗口依照前后顺序排布在这个 Z 轴上,Android 中这个模型叫 Z-Order,窗口值越大,在 WMS 中的优先级越高,最终在 Z 轴方向越靠近用户。后文详细介绍。

Android 11 前的栈管理

Android 11 前 ,在 AMS 的视角下,栈管理有三个概念:

  • ActivityRecord
  • TaskRecord
  • ActivityStack

而在 WMS 的视角,也有与之对应概念:

AMS 视角 WMS 视角 映射关系
ActivityRecord AppWindowToken
TaskRecord Task TaskId 映射
ActivityStack TaskStack StackId 映射

前言

很高兴见到你!👋

前面 我们讨论了 Activity 任务与返回栈。我在标题上使用了「老生常谈」这个词,是因为文中虽然使用了很多动画来展示 Activity 任务与返回栈的概念模型,但与网上的众多文章一样,仍旧是站在 Activity 的角度来讨论的(我 diss 我自己 🤪)。

不知你是否思考过以下问题:

  • 所有人都知道 Activity 可以展示 UI 界面,但 App 一定要有 Activity 吗?换句话说没有 Activity 就不能展示界面吗?
  • 状态栏和导航栏究竟是什么?
  • Activity 所谓的在前台,获得焦点,对用户可见等描述究竟是什么意思?
  • Android 中所谓的窗口是什么?
  • Android 中多窗口是如何实现的?
  • Android 中可以像桌面操作系统一样随意控制窗口的大小吗?

本文将以另一种视角来讨论 Activity 任务与返回栈,会涉及一些源码逻辑,源码部分主要基于 Android 10 与 Android 11。

涉及源码的部分已在标题中标记,如果觉得源码过多影响阅读体验,可以先跳过带有源码的部分,以理解概念为主,将主要概念理解后再回头查看。😉

前置知识

  • Android Detail:官方文档不是圣经,老生常谈的 Activity 任务,返回栈

  • 重学安卓:Activity 的快乐你不懂!

👆 强烈建议阅读,而且要多读几遍!

  • ActivityManagerService 与 WindowManagerService 简单了解

文章目录一览

  • 前言
  • 前置知识
  • 文章目录一览
  • App 一定要有 Activity 吗?
  • 状态栏与导航栏
    • 状态栏与导航栏是通过 Window 直接管理的
    • 状态栏与导航栏的显示与隐藏
  • 常见的 Window
  • 为何要用 Activity 和 Window 两个视角解读?
  • 多任务与多窗口
    • 开启自由窗口
    • WindowMode
  • AMS 与 WMS 对应的概念
    • ActivityType 与 WindowType
    • Android 11 前的栈管理

App 一定要有 Activity 吗?

不一定。其实这个问题很简单,因为存在一些没有界面的 app,例如源码中的 SettingsProvider,它仅有一个 Provider。

SettingsProvider 在设备的这个 👇 位置,感兴趣的小伙伴可以反编译一下,的确只有一个 Provider。

/system/priv-app/SettingsProvider/SettingsProvider.apk

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

SettingsProvider 位置

前面提到的是没有界面的 app ,那如果一个有 UI 界面的 app 一定要有 Activity 吗?

思考 ing ~ 🤔💭


答案也是不一定。

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

没有 Activity 的带有界面的 app

上图是一个只有一个 Service 的 app,通过 adb 命令启动 Service 后显示了一个全屏界面。

操作步骤:

  1. 安装根目录 window.apk
  2. 为该 app 开启悬浮窗权限
  3. adb 执行该命令 adb shell am startservice -n flywith24.android.detail.window/com.flywith24.detail.MainService

app 地址

有经验的小伙伴已经猜到,这个所谓的界面就是一个悬浮窗。

现在出现了一个问题:Android 中 Activity 不是显示 UI 的必要条件,是什么呢?

重学安卓:Activity 的快乐你不懂! 中早给出了答案:Window

在 Android 中,暴露给开发者操作 UI 界面的 API 是 mWindowManager.addView(rootView, windowParams);

实际上绕过 Window 还可以有更底层的实现方式,这里就不深入展开了。

状态栏与导航栏

如果觉得上一节的切入点比较抽象的话,我们以一个更加常用的入口来切入——状态栏与导航栏。

Android 中(特别是手机,TV 等特殊设备可能不需要),状态栏和导航栏是十分重要的组件。

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

System bars

我们通过 adb shell dumpsys window displays 命令输出当前的 display 情况,找到 WindowInsetsStateController 关键字,可以看到 顶部状态栏底部导航栏 所占的区域(基于 Android 11):

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

(0,0)(1080,66) 所占的区域为顶部状态栏;

(0,2028)(1080,2160) 所占的区域为底部导航栏;

一般来说,剩余部分是应用程序显示的区域,当然应用程序可以隐藏状态栏与导航栏以实现全屏显示的效果。

状态栏与导航栏是通过 Window 直接管理的

状态栏与导航栏是由 SystemUI 来进行管理的。

SystemUI 是一个系统级 app,为用户提供系统级别的信息显示与交互的一套 UI 组件,除了状态栏与导航栏,它还管理着壁纸,截图,最近任务列表(9.0 后在 Launcher 中)等组件。

而本质上,状态栏和导航栏都是通过 Window 直接管理的

源码中状态栏和导航栏的 View 与控制器分别为:

  • com.android.systemui.statusbar.phone.StatusBarWindowView

  • com.android.systemui.statusbar.phone.NavigationBarView

  • com.android.systemui.statusbar.phone.StatusBarWindowController

  • com.android.systemui.statusbar.NavigationBarController

其中 StatusBarWindowController 的 attach() 方法将 StatusBarWindowView 添加至 WindowManager

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

导航栏的内容稍显复杂(导航栏不仅显示三大金刚键,旋转时导航栏有旋转按钮,键盘弹出时有键盘按钮),它的添加逻辑被封装在 com.android.systemui.statusbar.phone.NavigationBarFragment 中,NavigationBarFragment 中的 create() 方法将 NavigationBarView 添加到 WindowManager。最终使用 fragment 的 onCreateView 加载布局。

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

状态栏与导航栏的显示与隐藏

官方为开发者提供了一套管理状态栏和导航栏的 API,通过 View 中的 setSystemUiVisibility() 方法即可管理。

但该 API 在 Android 11 中弃用了,取而代之的是使用 WindowInsetsController 统一管理。

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

如何显示/隐藏状态栏与导航栏

下图演示了显示/隐藏 状态栏,导航栏,软键盘 的操作:

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

管理状态栏、导航栏、软键盘

常见的 Window

开发中我们经常接触到的 Window 有 PopupWindow ,Dialog,Toast 自定义的悬浮窗等。

PopupWindow 中的 invokePopup() 方法内部调用了 mWindowManager.addView(decorView, p);

dialog 的 show() 方法中调用了 mWindowManager.addView(mDecor, l);

显示 Toast 的逻辑稍微复杂些,最终会执行 ToastPresenter#show() 方法,里面调用了 mWindowManager.addView(mView, mParams);

为何要用 Activity 和 Window 两个视角解读?

这里要引出两个系统服务:

  • ActivityManagerService,后文简称 AMS
  • WindowManagerService,后文简称 WMS

以上两个服务运行在 system_server 进程中。

这部分可以理解为一个 C/S 模型,App 为 客户端,system_server 为服务端。App 端调用的很多 API 都是发送到 system_server 进程中的系统服务中处理的。此处不详细展开。

AMS 虽然名字中有着 Activity,但其负责了 Android 中四大组件的管理。之前 我们介绍了 dumpsys 的原理,其中 adb shell dumpsys activity 输出的便是 AMS 的信息。

我们可以使用以下命令来输出四大组件的详细信息:

  • dumpsys activity activities
  • dumpsys activity broadcasts
  • dumpsys activity services
  • dumpsys activity providers

WMS 主要负责 Window 的管理,它的主要职责有:

  • 启动/隐藏窗口
  • 窗口动画
  • 窗口大小
  • 窗口层级
  • 事件派发

从 AMS 的视角只能分析任务与返回栈的逻辑概念,但应用程序都是以可视化的界面展示的,所以我们还需从 WMS 的角度再次解读这部分内容。

这更像是对已知存在事物 「如何实现」 的推演,至于对「为何存在如此设计」的追溯,重学安卓:Activity 的快乐你不懂! 中已详细介绍。

这也正是两个专栏存在的意义,本专栏 专注于前者,重学安卓 专注于后者。

此处只是引入这两个概念,视图系统系列会对这部分进行详细介绍。

所谓的 Activity 与 Window 视角便是 AMS 与 WMS 视角,后文以此描述。

多任务与多窗口

Android Detail:官方文档不是圣经,老生常谈的 Activity 任务,返回栈 中我们强调了 Android 是基于多任务理念设计的,而多任务更像是一种抽象的逻辑概念。在桌面操作系统中,一个屏幕中显示多个窗口是十分常见的使用场景。而在移动操作系统中,受限于移动设备屏幕尺寸,Android 在 7.0 才开始支持分屏。多窗口是一个相对比较好理解的概念,因为它有着可视化的表现方式。

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

Android 中的分屏

多任务是逻辑上的概念,多窗口是视觉上的概念。

Android 中的多窗口分为四种模式:

  • 全屏

  • 分屏(7.0 引入)

    Android Detail:站在 Window 视角理解 Activity 任务与返回栈

    分屏模式

  • 画中画(8.0 引入)

    Android Detail:站在 Window 视角理解 Activity 任务与返回栈

    画中画模式

  • FreeForm (自由窗口)

    Android Detail:站在 Window 视角理解 Activity 任务与返回栈

    自由窗口模式

分屏和画中画开启方式比较常见,下面主要介绍如何开启自由窗口。

开启自由窗口

开启自由窗口模式,需要执行以下命令:

adb shell settings put global enable_freeform_support 1

然后重启手机

在高版本上可以通过开发者选项来开启

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

在高版本设备通过开发者选开启自由窗口

接着,在 最近任务 界面长按应用图标便出现了自由窗口的选项。

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

自由窗口选项

WindowMode

为了区别不同的窗口模式,Android 中引入了 WindowMode 的概念:

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

最常见的模式为全屏,分屏状态下的两个窗口模式分别为 「分屏-主」,「分屏-次」,自由窗口也有其特殊的窗口模式。

AMS 与 WMS 对应的概念

ActivityType 与 WindowType

为了方便区分 Activity 和 Window 的类型,Android 引入了 ActivityType 与 WindowType 的概念,下表展示了主要的类型值及其含义:

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

最常见的 Activity 类型是标准类型,Launcher 有着独特的 Home 类型。

当栈顶的 Activity 类型为 Home 时,即使栈中有其他 Task,点击返回键也不能切换,即在桌面点击返回键不会退出应用显示栈中的下一个应用。这也是设计 ActivityType 的主要原因

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

设计 WindowType 是为了配置窗口的显示顺序考虑的。

Android 中主要有三种窗口类型:

  • App Window:取值范围为 1—99
  • Sub Window:取值范围为 1000—1999
  • System Window:取值范围为 2000—2999

众所周知,Android 中以屏幕左上角为原点,向右为 X 轴的正方向,向下为 Y 轴的正方向建立了一个二维空间。为了方便管理窗口的显示次序,屏幕被扩展为一个三维空间,定义了一个 Z 轴,方向垂直于屏幕表面指向屏幕外。多个窗口依照前后顺序排布在这个 Z 轴上,Android 中这个模型叫 Z-Order,窗口值越大,在 WMS 中的优先级越高,最终在 Z 轴方向越靠近用户。后文详细介绍。

Android 11 前的栈管理

Android 11 前 ,在 AMS 的视角下,栈管理有三个概念:

  • ActivityRecord
  • TaskRecord
  • ActivityStack

而在 WMS 的视角,也有与之对应概念:

AMS 视角 WMS 视角 映射关系
ActivityRecord AppWindowToken
TaskRecord Task TaskId 映射
ActivityStack TaskStack StackId 映射

前言

很高兴见到你!👋

前面 我们讨论了 Activity 任务与返回栈。我在标题上使用了「老生常谈」这个词,是因为文中虽然使用了很多动画来展示 Activity 任务与返回栈的概念模型,但与网上的众多文章一样,仍旧是站在 Activity 的角度来讨论的(我 diss 我自己 🤪)。

不知你是否思考过以下问题:

  • 所有人都知道 Activity 可以展示 UI 界面,但 App 一定要有 Activity 吗?换句话说没有 Activity 就不能展示界面吗?
  • 状态栏和导航栏究竟是什么?
  • Activity 所谓的在前台,获得焦点,对用户可见等描述究竟是什么意思?
  • Android 中所谓的窗口是什么?
  • Android 中多窗口是如何实现的?
  • Android 中可以像桌面操作系统一样随意控制窗口的大小吗?

本文将以另一种视角来讨论 Activity 任务与返回栈,会涉及一些源码逻辑,源码部分主要基于 Android 10 与 Android 11。

涉及源码的部分已在标题中标记,如果觉得源码过多影响阅读体验,可以先跳过带有源码的部分,以理解概念为主,将主要概念理解后再回头查看。😉

前置知识

  • Android Detail:官方文档不是圣经,老生常谈的 Activity 任务,返回栈

  • 重学安卓:Activity 的快乐你不懂!

👆 强烈建议阅读,而且要多读几遍!

  • ActivityManagerService 与 WindowManagerService 简单了解

文章目录一览

  • 前言
  • 前置知识
  • 文章目录一览
  • App 一定要有 Activity 吗?
  • 状态栏与导航栏
    • 状态栏与导航栏是通过 Window 直接管理的
    • 状态栏与导航栏的显示与隐藏
  • 常见的 Window
  • 为何要用 Activity 和 Window 两个视角解读?
  • 多任务与多窗口
    • 开启自由窗口
    • WindowMode
  • AMS 与 WMS 对应的概念
    • ActivityType 与 WindowType
    • Android 11 前的栈管理

App 一定要有 Activity 吗?

不一定。其实这个问题很简单,因为存在一些没有界面的 app,例如源码中的 SettingsProvider,它仅有一个 Provider。

SettingsProvider 在设备的这个 👇 位置,感兴趣的小伙伴可以反编译一下,的确只有一个 Provider。

/system/priv-app/SettingsProvider/SettingsProvider.apk

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

SettingsProvider 位置

前面提到的是没有界面的 app ,那如果一个有 UI 界面的 app 一定要有 Activity 吗?

思考 ing ~ 🤔💭


答案也是不一定。

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

没有 Activity 的带有界面的 app

上图是一个只有一个 Service 的 app,通过 adb 命令启动 Service 后显示了一个全屏界面。

操作步骤:

  1. 安装根目录 window.apk
  2. 为该 app 开启悬浮窗权限
  3. adb 执行该命令 adb shell am startservice -n flywith24.android.detail.window/com.flywith24.detail.MainService

app 地址

有经验的小伙伴已经猜到,这个所谓的界面就是一个悬浮窗。

现在出现了一个问题:Android 中 Activity 不是显示 UI 的必要条件,是什么呢?

重学安卓:Activity 的快乐你不懂! 中早给出了答案:Window

在 Android 中,暴露给开发者操作 UI 界面的 API 是 mWindowManager.addView(rootView, windowParams);

实际上绕过 Window 还可以有更底层的实现方式,这里就不深入展开了。

状态栏与导航栏

如果觉得上一节的切入点比较抽象的话,我们以一个更加常用的入口来切入——状态栏与导航栏。

Android 中(特别是手机,TV 等特殊设备可能不需要),状态栏和导航栏是十分重要的组件。

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

System bars

我们通过 adb shell dumpsys window displays 命令输出当前的 display 情况,找到 WindowInsetsStateController 关键字,可以看到 顶部状态栏底部导航栏 所占的区域(基于 Android 11):

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

(0,0)(1080,66) 所占的区域为顶部状态栏;

(0,2028)(1080,2160) 所占的区域为底部导航栏;

一般来说,剩余部分是应用程序显示的区域,当然应用程序可以隐藏状态栏与导航栏以实现全屏显示的效果。

状态栏与导航栏是通过 Window 直接管理的

状态栏与导航栏是由 SystemUI 来进行管理的。

SystemUI 是一个系统级 app,为用户提供系统级别的信息显示与交互的一套 UI 组件,除了状态栏与导航栏,它还管理着壁纸,截图,最近任务列表(9.0 后在 Launcher 中)等组件。

而本质上,状态栏和导航栏都是通过 Window 直接管理的

源码中状态栏和导航栏的 View 与控制器分别为:

  • com.android.systemui.statusbar.phone.StatusBarWindowView

  • com.android.systemui.statusbar.phone.NavigationBarView

  • com.android.systemui.statusbar.phone.StatusBarWindowController

  • com.android.systemui.statusbar.NavigationBarController

其中 StatusBarWindowController 的 attach() 方法将 StatusBarWindowView 添加至 WindowManager

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

导航栏的内容稍显复杂(导航栏不仅显示三大金刚键,旋转时导航栏有旋转按钮,键盘弹出时有键盘按钮),它的添加逻辑被封装在 com.android.systemui.statusbar.phone.NavigationBarFragment 中,NavigationBarFragment 中的 create() 方法将 NavigationBarView 添加到 WindowManager。最终使用 fragment 的 onCreateView 加载布局。

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

状态栏与导航栏的显示与隐藏

官方为开发者提供了一套管理状态栏和导航栏的 API,通过 View 中的 setSystemUiVisibility() 方法即可管理。

但该 API 在 Android 11 中弃用了,取而代之的是使用 WindowInsetsController 统一管理。

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

如何显示/隐藏状态栏与导航栏

下图演示了显示/隐藏 状态栏,导航栏,软键盘 的操作:

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

管理状态栏、导航栏、软键盘

常见的 Window

开发中我们经常接触到的 Window 有 PopupWindow ,Dialog,Toast 自定义的悬浮窗等。

PopupWindow 中的 invokePopup() 方法内部调用了 mWindowManager.addView(decorView, p);

dialog 的 show() 方法中调用了 mWindowManager.addView(mDecor, l);

显示 Toast 的逻辑稍微复杂些,最终会执行 ToastPresenter#show() 方法,里面调用了 mWindowManager.addView(mView, mParams);

为何要用 Activity 和 Window 两个视角解读?

这里要引出两个系统服务:

  • ActivityManagerService,后文简称 AMS
  • WindowManagerService,后文简称 WMS

以上两个服务运行在 system_server 进程中。

这部分可以理解为一个 C/S 模型,App 为 客户端,system_server 为服务端。App 端调用的很多 API 都是发送到 system_server 进程中的系统服务中处理的。此处不详细展开。

AMS 虽然名字中有着 Activity,但其负责了 Android 中四大组件的管理。之前 我们介绍了 dumpsys 的原理,其中 adb shell dumpsys activity 输出的便是 AMS 的信息。

我们可以使用以下命令来输出四大组件的详细信息:

  • dumpsys activity activities
  • dumpsys activity broadcasts
  • dumpsys activity services
  • dumpsys activity providers

WMS 主要负责 Window 的管理,它的主要职责有:

  • 启动/隐藏窗口
  • 窗口动画
  • 窗口大小
  • 窗口层级
  • 事件派发

从 AMS 的视角只能分析任务与返回栈的逻辑概念,但应用程序都是以可视化的界面展示的,所以我们还需从 WMS 的角度再次解读这部分内容。

这更像是对已知存在事物 「如何实现」 的推演,至于对「为何存在如此设计」的追溯,重学安卓:Activity 的快乐你不懂! 中已详细介绍。

这也正是两个专栏存在的意义,本专栏 专注于前者,重学安卓 专注于后者。

此处只是引入这两个概念,视图系统系列会对这部分进行详细介绍。

所谓的 Activity 与 Window 视角便是 AMS 与 WMS 视角,后文以此描述。

多任务与多窗口

Android Detail:官方文档不是圣经,老生常谈的 Activity 任务,返回栈 中我们强调了 Android 是基于多任务理念设计的,而多任务更像是一种抽象的逻辑概念。在桌面操作系统中,一个屏幕中显示多个窗口是十分常见的使用场景。而在移动操作系统中,受限于移动设备屏幕尺寸,Android 在 7.0 才开始支持分屏。多窗口是一个相对比较好理解的概念,因为它有着可视化的表现方式。

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

Android 中的分屏

多任务是逻辑上的概念,多窗口是视觉上的概念。

Android 中的多窗口分为四种模式:

  • 全屏

  • 分屏(7.0 引入)

    Android Detail:站在 Window 视角理解 Activity 任务与返回栈

    分屏模式

  • 画中画(8.0 引入)

    Android Detail:站在 Window 视角理解 Activity 任务与返回栈

    画中画模式

  • FreeForm (自由窗口)

    Android Detail:站在 Window 视角理解 Activity 任务与返回栈

    自由窗口模式

分屏和画中画开启方式比较常见,下面主要介绍如何开启自由窗口。

开启自由窗口

开启自由窗口模式,需要执行以下命令:

adb shell settings put global enable_freeform_support 1

然后重启手机

在高版本上可以通过开发者选项来开启

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

在高版本设备通过开发者选开启自由窗口

接着,在 最近任务 界面长按应用图标便出现了自由窗口的选项。

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

自由窗口选项

WindowMode

为了区别不同的窗口模式,Android 中引入了 WindowMode 的概念:

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

最常见的模式为全屏,分屏状态下的两个窗口模式分别为 「分屏-主」,「分屏-次」,自由窗口也有其特殊的窗口模式。

AMS 与 WMS 对应的概念

ActivityType 与 WindowType

为了方便区分 Activity 和 Window 的类型,Android 引入了 ActivityType 与 WindowType 的概念,下表展示了主要的类型值及其含义:

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

最常见的 Activity 类型是标准类型,Launcher 有着独特的 Home 类型。

当栈顶的 Activity 类型为 Home 时,即使栈中有其他 Task,点击返回键也不能切换,即在桌面点击返回键不会退出应用显示栈中的下一个应用。这也是设计 ActivityType 的主要原因

Android Detail:站在 Window 视角理解 Activity 任务与返回栈

设计 WindowType 是为了配置窗口的显示顺序考虑的。

Android 中主要有三种窗口类型:

  • App Window:取值范围为 1—99
  • Sub Window:取值范围为 1000—1999
  • System Window:取值范围为 2000—2999

众所周知,Android 中以屏幕左上角为原点,向右为 X 轴的正方向,向下为 Y 轴的正方向建立了一个二维空间。为了方便管理窗口的显示次序,屏幕被扩展为一个三维空间,定义了一个 Z 轴,方向垂直于屏幕表面指向屏幕外。多个窗口依照前后顺序排布在这个 Z 轴上,Android 中这个模型叫 Z-Order,窗口值越大,在 WMS 中的优先级越高,最终在 Z 轴方向越靠近用户。后文详细介绍。

Android 11 前的栈管理

Android 11 前 ,在 AMS 的视角下,栈管理有三个概念:

  • ActivityRecord
  • TaskRecord
  • ActivityStack

而在 WMS 的视角,也有与之对应概念:

AMS 视角 WMS 视角 映射关系
ActivityRecord AppWindowToken
TaskRecord Task TaskId 映射
ActivityStack TaskStack StackId 映射

部分转自互联网,侵权删除联系

赞(0) 打赏
部分文章转自网络,侵权联系删除b2bchain区块链学习技术社区 » Android Detail:站在 Window 视角理解 Activity 任务与返回栈求职学习资料
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

b2b链

联系我们联系我们