前言

本文主要涉及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. 51nod1089 最长回文子串 manacher算法

    0. 问题定义 最长回文子串问题:给定一个字符串,求它的最长回文子串长度. 如果一个字符串正着读和反着读是一样的,那它就是回文串.下面是一些回文串的实例: 12321 a aba abba aaaa ...

  2. 深入 Python 解释器源码,我终于搞明白了字符串驻留的原理!

    英文:https://arpitbhayani.me/blogs/string-interning 作者:arpit 译者:豌豆花下猫("Python猫"公众号作者) 声明:本翻译 ...

  3. how to enable vue cli auto open the localhost url

    how to enable vue cli auto open the localhost URL bad you must click the link by manually, waste of ...

  4. HTML spaces types:   &   &  

    HTML spaces types:   &   &   What is the difference between   and   https://stackoverflow.co ...

  5. 时间轴 timeline

    时间轴 timeline https://www.helloweba.net/javascript/285.html https://www.helloweba.net/demo/v_timeline ...

  6. Headless Chrome

    Headless Chrome https://developers.google.com/web/updates/2017/04/headless-chrome Puppeteer & SS ...

  7. DENIEL SOIBIM:如何保持坚持

    丹尼尔·索比姆作为加州理工高材生,在2005年通过创建投资俱乐部对潜力公司进行天使投资,获得了美国Blue Run高层的重视,并相继担任Blue Run潜力营收专家评估师,2009年成为星盟集团的副总 ...

  8. PyQt5 点不着的按钮

    1 import sys 2 import typing 3 4 from PyQt5 import QtWidgets, QtGui, QtCore 5 import random 6 7 clas ...

  9. 字节码增强技术-Byte Buddy

    本文转载自字节码增强技术-Byte Buddy 为什么需要在运行时生成代码? Java 是一个强类型语言系统,要求变量和对象都有一个确定的类型,不兼容类型赋值都会造成转换异常,通常情况下这种错误都会被 ...

  10. DRF的orm多表关系补充及serializer子序列化

    目录 一.控制多表关系的字段属性 1.如何建立基表 2.断开连表关系 3.四种级联关系 二.子序列化 一.控制多表关系的字段属性 1.如何建立基表 要在基表中配置Meta,设置abstract=Tru ...