前言

本文主要涉及android系统对于activity的组织管理。activity是死的,只有在系统的调度下,才在手机上呈现各种各样的界面,而有那么多的activity,系统是以什么样的规则去管理调度则是一个值得深入探究的问题。

首先介绍几个概念:

  • 什么是Task?

    task翻译过来就是任务,好比用户在使用不同的app是在做不同的任务,而一个app一般有多个activity,所以有必要根据不同的任务来进行activity的管理。
  • 什么是Stack?

    学过数据结构都知道Stack是一种先进后出的数据结构,用stack来管理activity是理所当然的。当切换到一个新的界面时,该activity被push到栈顶,当移除时新的栈顶则变成前一个activity。
  • 什么是ActivityStack?Android使用ActivityStack来管理task,一个ActivityStack由不同的任务组成;

如何观察ActivityStack?

可以通过adb shell dumpsys activity activities观察ActivityStack等活动状态,这儿可以看到实际的组织如前面所说,ActivityStack包含Task,而Task包含Activity;如果对应到AMS中的数据结构,那么就是ActivityStack、TaskRecord、ActivityRecord,下面将从代码层面讲述这几个关键类。

几个问题

  • 同一个应用的Activity一定在同一个task里吗?
  • 返回和startActivity一样吗?
  • 普通任务栈与Stack #0无关,为什么返回到最后退到了launcher?
  • android:launchMode和intent.flag有什么区别与联系?
  • 既然每一个taskrecord都有栈顶的ActivityRecord,那到底哪个task才是在前台的?
  • 各种模式最佳应用场景?为什么要区分这四种启动模式?
  • TaskAffinity是什么?对哪些模式有影响?

关键类介绍

Ams中关于Activity管理的几个关键类关系如下所示

  • ActivityStackSupervisor

在AMS构造的时候就会创建,全局唯一;管理所有的ActivityStack,同时在启动一个Activity的流程中也有重要作用,这个之后分析。

  • ActivityStack

    管理任务栈。有多个,其中一开机就会有Stack #0,Launcher所在的Stack
  • TaskRecord 记录任务,管理Activity
  • ActivityRecord 一个Activity实例在Ams中的记录,同一个Activity类可能有多个记录存在

ActivityStack的创建与种类

./frameworks/base/core/java/android/app/ActivityManager.java

        /** Invalid stack ID. */
public static final int INVALID_STACK_ID = -1; /** 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; /** Last static stack stack ID. */
public static final int LAST_STATIC_STACK_ID = PINNED_STACK_ID; /** Start of ID range used by stacks that are created dynamically. */

最常用的是HOME_STACK_ID即Stack #0和一般Stack #1。每次开机的时候即会创建Stack #0,这是launcher和systemui的RecentsActivity所在的Stack;当从launcher点击一个图标进入新的app后,则会创建stack #1以及相应的taskrecord

不同启动模式

不同的启动模式主要涉及到ActivityRecord的管理。Android并没有简单地对Activity进行栈式的“先入后出”管理,因为实际的使用各个应用间的Activity组件可以相互调用,同时根据实际的需求又衍生出了各种启动模式。有了上面背景知识的铺垫,可以更好的理解各种launchMode和Intent的flag具体含义。

launchMode

Standard

这也是默认模式,含义是每次startActivity时都会创建一个新的实例,并且走完onCreate、onStart和onResume 。值得注意的是两点:

  1. 该ActivityRecord会放在启动它的Activity所在的任务栈的栈顶。
  2. 可以在同一个任务栈出现多个实例
  3. 如果在没有任务栈的情况下启动 standard 模式的 Activity,比如在 Service 中,此时新的 Activity 没有任务栈可入,会出现异常:
Caused by: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

此时应该为这个 Activity 指定 FLAG_ACTIVITY_NEW_TASK,这样就会新建一个任务栈。

SingleTop

顾名思义,SingleTop的含义是在栈顶只存在一个同样的Activity实例,即当startActivity自身的时候,如果该Activity已经存在在栈顶,那么并不重新创建一个实例,而是调用其onNewIntent回调。注意这仅是SingleTop,是允许栈中间存在相同实例的。

前面两种情况其实都是针对同一个任务栈来说的,并且要么是该任务栈不存在需要创建,要么就是前一个Activity所在的任务栈。接下来将说一下可能涉及到不同任务栈的情况

SingleTask

SingleTask是一种栈内复用模式,比前面两种复杂一些。首先寻找需要的任务栈,需要的含义是通过TaskAffinity指定的任务栈名字,默认就是包名。如果不存在,则创建相应的任务栈并将Activity放入栈顶;如果存在需要的任务栈,再看是不是位于栈顶,如果位于栈顶则只需要调用onNewIntent,如果不位于栈顶则pop出在此Activity之上的活动,让此Activity到栈顶。所以SingleTask可能会影响到其他Activity的生命周期。

SingleInstance

在一个新栈中放入该Activity,并且这个栈只能存放这一个Activity;一旦SingleInstance模式的Activity已经存在,则会一直复用这个Activity实例。

回顾问题

我们在前言中提到了几个问题,现在再回过头来看一下。

  • 同一个应用的Activity一定在同一个task里吗?
显然不是。一个Activity组件可以存在于调用者的任务栈中,也可以通过TaskAffinity去设置需要的任务栈,也可以设置SingleInstance单独存在。
  • 返回和startActivity的区别?
onBackPressed最终调用了finish,是会将当前Activity从栈顶移除的;而startActivity则根据启动模式的设置,依照规则去操作任务栈。
  • 普通任务栈与Stack #0无关,为什么返回到最后退到了launcher?
  • android:launchMode和intent.flag有什么区别与联系?
launchMode是规定你自己的Activity启动的行为模式,而Intent.Flag是你期望由你启动的其他的Activity是什么样的行为模式。
所以这两者都能参与任务栈的管理,只是负责的对象不同。但是组合起来就很多种情况了,比较复杂。
  • 既然每一个taskrecord都有栈顶的ActivityRecord,那到底哪个task才是在前台的?
ActivityStackSupervisor中有变量mFocusedStack来标识哪个任务栈是当前使用的。
Ams中有mFocusedActivity来记录当前的focus Activity。
  • 各种模式最佳应用场景?为什么要区分这四种启动模式?
各种模式的存在肯定是有必要的,因为android组件化的思想使得弱化单独进程的概念。各个app内的组件可以相互调用,更多的规则组合可以兼顾需求和性能。
singleTop:适合启动同类型的 Activity,例如接收通知启动的内容显示页面
singleTask:适合作为程序入口
singleInstance:适合需要共享出去的界面
  • TaskAffinity是什么?对哪些模式有影响?
设置任务亲和性,上文已说主要是用于指定Activity需要的任务栈。
实际使用中一般配合SingleTask或SingleInstance使用。

Android中的TaskStack及启动模式的更多相关文章

  1. Android中Activity的四大启动模式实验简述

    作为Android四大组件之一,Activity可以说是最基本也是最常见的组件,它提供了一个显示界面,从而实现与用户的交互,作为初学者,必须熟练掌握.今天我们就来通过实验演示,来帮助大家理解Activ ...

  2. Activity中的四种启动模式

    在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启动模式决定了Activity的启动运行方式. An ...

  3. Android Activity 的四种启动模式 lunchMode 和 Intent.setFlags();singleTask的两种启动方式。

    原文:Android Activity 的四种启动模式 lunchMode 和 Intent.setFlags();singleTask的两种启动方式. Android Activity 的四种启动模 ...

  4. Android Activity的4种启动模式详解(示例)

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/5233269.html 先介绍下Android对Activity的管理,Android采用Task来管理多个A ...

  5. Android:图解四种启动模式 及 实际应用场景解说

    在一个项目中会包括着多个Activity,系统中使用任务栈来存储创建的Activity实例,任务栈是一种“后进先出”的栈结构.举个栗子,若我们多次启动同一个Activity.系统会创建多个实例依次放入 ...

  6. Android多任务切换与Activity启动模式SingleTask之间关系的分析

    这里会以多个场景列子进行分析,在分析之前先了解一下基本的概念. Task任务:一系列Activity的集合,这些Activity以栈的形式进行排列(后进先出). 那在什么时候系统会新建一个Task任务 ...

  7. Android开发9——Activity的启动模式

    在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启动模式决定了Activity的启动运行方式. 一. ...

  8. android(十四)四种启动模式

    standard 启动的activity会每次都重新创建一个activity放到任务栈中.这是系统默认的启动模式. singleTop启动的activity,如果任务的栈顶刚好存在当前的activit ...

  9. Android Activity生命周期及启动模式

    曾经搞过许多东西,再熟练的东西一段时间没有碰都会生疏或忘记.后来体会到写成文档记录下来的重要性,但有些word或ppt记录下来的东西随着时间流逝会丢失,或者不愿去看.或许保存成博客的形式,会是更好的选 ...

随机推荐

  1. u-boot 移植 --->2、在u-boot新增SOC和板子

    本次主要是要新增一个samsung的芯片到u-boot中,网上查阅资料发现s5pc1xx是与手上的S5PV210的友善的Tiny版子寄存器兼容的比较多,所以就准备以他为基础增加一个我的板子的支持到u- ...

  2. vue2 响应式细节

    data 中的数据是如何处理的? 每一次实例化一个组件,都会调用 initData 然后调用 observe 方法,observe 方法调用了 new Observer(value), 并且返回 __ ...

  3. 虚拟环境之间批量pip安装包"迁移"

    在某个虚拟环境中通过 pip freeze > requirements.txt 将该环境下所有的包写入文档, 然后切换至另一虚拟环境, pip install -r requirements. ...

  4. HBuilderX All In One

    HBuilderX All In One uni-app https://uniapp.dcloud.io/quickstart-hx 目录结构 一个uni-app工程,默认包含如下目录及文件: $ ...

  5. Sentry React SourceMaps All In One

    Sentry React SourceMaps All In One React https://docs.sentry.io/platforms/javascript/guides/react/ h ...

  6. vue-cli & plugin:vue/strongly-recommended bug

    vue-cli & plugin:vue/strongly-recommended bug ESLint plugin:vue/strongly-recommended module.expo ...

  7. ES6 Map All In One

    ES6 Map All In One Map 字典/地图 Set 集合 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Referenc ...

  8. 如何取消 Google Cloud Platform 试用 & 关闭 GCP 帐号 & 删除信用卡 & 取消订阅

    如何取消 Google Cloud Platform 试用 & 关闭 GCP 帐号 & 删除信用卡 & 取消订阅 关闭您的 Google Cloud Platform 帐号 s ...

  9. Rollup & Webpack & Parcel

    Rollup & Webpack & Parcel package bundler https://x-team.com/blog/rollup-webpack-parcel-comp ...

  10. nodejs ECMAScript 模块

    node 文档 main.mjs import path from 'path'; import logger from './logger.js'; logger.show('hello world ...