探索Activity之launchMode

之前有简单探索了Activity的生命周期, 也提到, Activity的生命周期实际场景远非那么简单, 诸如launch mode, intent flag, activity属性等都会对生命周期流程产生影响.

本想将launchMode, intent flag, activity任务相关属性(affinity, clearTaskOnLaunch, finishOnTaskLaunch等)一起结合task和back stack探究以下, 发现关于intent flag的使用和官方文档有很多出入, 在此先探索下启动模式launchMode.

google发现, 国外很多人对Android官方文档的task and back stack这块有质疑, 也有Android的开发人员出来解释是由于代码更新, 文档没有跟上~~
看来果然是尽信书不如无书啊, 下次结合实例深入研究下这块.

1, 相关概念

根据官方解释:

The launchMode attribute specifies an instruction on how the activity should be launched into a task.

大意是说launchMode指示一个activity是以何种形式被启动(放置)到一个task中的.
还是涉及到task了, 先了解下吧:

  • Task
    • 任务
    • 是指在执行特定作业时与用户交互的一系列 Activity.
  • Back Stack
    • 返回栈
    • 组成任务的这些Activity按照各自的打开顺序排列在堆栈中.

以上是官方文档的解释.
这里有一个疑问, Task和Back Stack的对应关系是怎样的呢, 一一对应, 还是说系统就一个back stack, 所有的activity都在stack中呢? 这个疑问我们在探索launchMode的过程中也会有解答~

  • launchMode

    • 有四种(standard, singleTop, singleTask, singleInstance), 默认standard.
  • standard

    • Activity 可以多次实例化,而每个实例均可属于不同的任务,并且一个任务可以拥有多个实例。
  • singleTop

    • 如果当前任务的顶部已存在 Activity 的一个实例,则系统会通过调用该实例的 onNewIntent() 方法向其传送 Intent,而不是创建 Activity 的新实例.
    • Activity 可以多次实例化,而每个实例均可属于不同的任务,并且一个任务可以拥有多个实例.
  • singleTask

    • 系统创建新任务并实例化位于新任务底部的 Activity. 但是,如果该 Activity 的一个实例已存在于一个单独的任务中,则系统会通过调用现有实例的 onNewIntent() 方法向其传送 Intent,而不是创建新实例.
    • 一次只能存在 Activity 的一个实例.
  • singleInstance

    • 与 "singleTask" 相同,只是系统不会将任何其他 Activity 启动到包含实例的任务中.
    • 该 Activity 始终是其任务唯一仅有的成员;由此 Activity 启动的任何 Activity 均在单独的任务中打开.

以上概念解释都来自官方文档.

我们接下来要做的就是编写实例加以验证, 并对我们之前探索的Activity的生命周期加以补充.

2, 开始探索

借用上次探索生命周期的Demo程序.
Github源码地址

我们有三个activity: AActivity, BActivity, CActivity.
下面我们围绕这三个activity开展一系列实验.

2.1, Standard模式

2.1.1, 都为standard, 执行A -> B -> C -> C

生命周期Log:


探索Activity之launchMode_第1张图片

Task/Back Stack信息:

  Stack #1 mStackId=55:
    Task id #193
    * TaskRecord{4325d0a0 #193 A=com.anly.samples U=0 sz=5}
      numActivities=5 rootWasReset=true userId=0 mTaskType=0 numFullscreen=5 mOnTopOfHome=true
      affinity=com.anly.samples
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.anly.samples/.MainActivity}
      realActivity=com.anly.samples/.MainActivity
      Activities=[
      ActivityRecord{432654c8 u0 com.anly.samples/.MainActivity t193},
      ActivityRecord{4366fbd0 u0 com.anly.samples/.activity.AActivity t193}, 
      ActivityRecord{429e0cc0 u0 com.anly.samples/.activity.BActivity t193}, 
      ActivityRecord{43590f20 u0 com.anly.samples/.activity.CActivity t193}, 
      ActivityRecord{427f6d98 u0 com.anly.samples/.activity.CActivity t193}]

可以看到:
1, C启动C时, 新建了一个C的实例.
2, 此时Task #193 中的实例如同我们的启动顺序依次是ABCC.

2.2 SingleTop模式

2.2.1, C设置为singleTop, 执行A -> B -> C -> C

生命周期Log:


探索Activity之launchMode_第2张图片

Task/Back Stack信息:

Stack #1 mStackId=24:
    Task id #155
    * TaskRecord{432ae270 #155 A=com.anly.samples U=0 sz=5}
      numActivities=5 rootWasReset=false userId=0 mTaskType=0 numFullscreen=5 mOnTopOfHome=true
      affinity=com.anly.samples
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.anly.samples/.MainActivity}
      realActivity=com.anly.samples/.MainActivity
      Activities=[ActivityRecord{427bf238 u0 com.anly.samples/.MainActivity t155}, 
      ActivityRecord{427c1c28 u0 com.anly.samples/.activity.AActivity t155}, 
      ActivityRecord{427c2b80 u0 com.anly.samples/.activity.BActivity t155}, 
      ActivityRecord{427c34c8 u0 com.anly.samples/.activity.CActivity t155}]

可以看到:
1, C启动C时, 并未重建一个C, 还是使用了之前的实例, 通过onNewIntent的方式唤起.
2, 需要注意的时, 就算是C复用了, 还是会执行C的onPause, 然后再onNewIntent --> onResume的.
3, 此时Task #155的Activity实例顺序是ABC.

2.2.2, B设置为singleTop, 执行A -> B -> C -> B

生命周期Log:


探索Activity之launchMode_第3张图片

Task/Back Stack信息:

  Stack #1 mStackId=57:
    Task id #195
    * TaskRecord{42fe9718 #195 A=com.anly.samples U=0 sz=5}
      numActivities=5 rootWasReset=false userId=0 mTaskType=0 numFullscreen=5 mOnTopOfHome=true
      affinity=com.anly.samples
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.anly.samples/.MainActivity}
      realActivity=com.anly.samples/.MainActivity
      Activities=[ActivityRecord{427f48c8 u0 com.anly.samples/.MainActivity t195}, 
      ActivityRecord{43355cd8 u0 com.anly.samples/.activity.AActivity t195}, 
      ActivityRecord{429984d8 u0 com.anly.samples/.activity.BActivity t195}, 
      ActivityRecord{4484a590 u0 com.anly.samples/.activity.CActivity t195}, 
      ActivityRecord{43953fe8 u0 com.anly.samples/.activity.BActivity t195}]

可以看到:
1, 从C启动B时, 又新建了一个B的实例.
2, 此时Task #195的Activity实例顺序为ABCB, 看起来, 这个和2.1.1的standard模式实验一样, 都是新建了实例.

2.3 SingleTask模式

2.3.1 设置C为singleTask, 执行A -> B -> C -> C

生命周期Log:


探索Activity之launchMode_第4张图片

Task/Back Stack信息:

Stack #1 mStackId=26:
    Task id #157
    * TaskRecord{43025e70 #157 A=com.anly.samples U=0 sz=4}
      numActivities=4 rootWasReset=false userId=0 mTaskType=0 numFullscreen=4 mOnTopOfHome=true
      affinity=com.anly.samples
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.anly.samples/.MainActivity}
      realActivity=com.anly.samples/.MainActivity
      Activities=[ActivityRecord{427bbd48 u0 com.anly.samples/.MainActivity t157}, 
      ActivityRecord{43bc91c8 u0 com.anly.samples/.activity.AActivity t157}, 
      ActivityRecord{42a239b8 u0 com.anly.samples/.activity.BActivity t157}, 
      ActivityRecord{430e21c0 u0 com.anly.samples/.activity.CActivity t157}]

可以看到结果和2.2.1一样
1, C复用了. Task里的Activity依次为ABC.
2, C先onPause, 然后onNewIntent唤起, 走onResume.

2.3.2, 设置B为singleTask, 执行A -> B -> C -> B

生命周期Log:


探索Activity之launchMode_第5张图片

Task/Back Stack信息:

Stack #1 mStackId=27:
    Task id #158
    * TaskRecord{42ccb098 #158 A=com.anly.samples U=0 sz=3}
      numActivities=3 rootWasReset=false userId=0 mTaskType=0 numFullscreen=3 mOnTopOfHome=true
      affinity=com.anly.samples
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.anly.samples/.MainActivity}
      realActivity=com.anly.samples/.MainActivity
      Activities=[ActivityRecord{427bbd48 u0 com.anly.samples/.MainActivity t158}, 
      ActivityRecord{43082eb0 u0 com.anly.samples/.activity.AActivity t158}, 
      ActivityRecord{43aaea90 u0 com.anly.samples/.activity.BActivity t158}]

可以看到:
1, B是复用的, onNewIntent唤起, 走的onRestart流程.
2, Task #158的Activity实例依次是AB.
3, C没有主动销毁(Back, finish), 但是被移除了.

2.4, SingleInstance模式

2.4.1, 设置C为singleInstance, 执行A -> B -> C -> C

生命周期Log:


探索Activity之launchMode_第6张图片

Task/Back Stack信息:

Stack #1 mStackId=31:
    Task id #163
    * TaskRecord{43308db8 #163 A=com.anly.samples U=0 sz=1}
      numActivities=1 rootWasReset=false userId=0 mTaskType=0 numFullscreen=1 mOnTopOfHome=false
      affinity=com.anly.samples
      intent={cmp=com.anly.samples/.activity.CActivity}
      realActivity=com.anly.samples/.activity.CActivity
      Activities=[ActivityRecord{432f03c0 u0 com.anly.samples/.activity.CActivity t163}]

    Task id #162
    * TaskRecord{42d702b0 #162 A=com.anly.samples U=0 sz=3}
      numActivities=3 rootWasReset=false userId=0 mTaskType=0 numFullscreen=3 mOnTopOfHome=true
      affinity=com.anly.samples
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.anly.samples/.MainActivity}
      realActivity=com.anly.samples/.MainActivity
      Activities=[ActivityRecord{427bc7e8 u0 com.anly.samples/.MainActivity t162}, 
      ActivityRecord{42995d98 u0 com.anly.samples/.activity.AActivity t162}, 
      ActivityRecord{4326ee40 u0 com.anly.samples/.activity.BActivity t162

可以看到:
1, 第一次B启动C时, C运行在了另一个Task #163中.
2, 从C再次启动C室, C复用了, 类似的, onNewIntent唤起, 走onResume流程.
3, 特别注意: task信息中, 可以看到Stack和Task的关系. 此时, Stack #1中有两个Task, 分别是Task #162(AB)在Stack底部, Task #163(C)在Stack顶部.

2.4.2, 设置A为singleInstance, 执行A -> B -> C -> A

生命周期Log:


Task/Back Stack信息:

Stack #1 mStackId=34:
    Task id #169
    * TaskRecord{431fd9e0 #169 A=com.anly.samples U=0 sz=1}
      numActivities=1 rootWasReset=false userId=0 mTaskType=0 numFullscreen=1 mOnTopOfHome=false
      affinity=com.anly.samples
      intent={flg=0x400000 cmp=com.anly.samples/.activity.AActivity}
      realActivity=com.anly.samples/.activity.AActivity
      Activities=[ActivityRecord{43a58140 u0 com.anly.samples/.activity.AActivity t169}]

    Task id #168
    * TaskRecord{437328f0 #168 A=com.anly.samples U=0 sz=3}
      numActivities=3 rootWasReset=true userId=0 mTaskType=0 numFullscreen=3 mOnTopOfHome=false
      affinity=com.anly.samples
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.anly.samples/.MainActivity}
      realActivity=com.anly.samples/.MainActivity
      Activities=[ActivityRecord{43114528 u0 com.anly.samples/.MainActivity t168}, 
      ActivityRecord{42ff3f20 u0 com.anly.samples/.activity.BActivity t168}, 
      ActivityRecord{42abe0a0 u0 com.anly.samples/.activity.CActivity t168}]

可以看到结果和2.4.1类似
1, A单启在一个Task #169中, 再次启动不重建, onNewIntent唤起, 走onRestart流程.
2, 需要注意的是, 从A启动B的时候, B是跟MainActiviy在一个task的, 也就是说A所在的task容不下别人.
3, 需要强调的是, A是复用的, 这个时候如果一直按Back键返回, 不会再看到A了, 如下:

探索Activity之launchMode_第7张图片

结论

根据以上实验, 可以得出以下结论:

  1. standard模式的Activity, 每次启动都会创建一个新的实例, 放到启动他的那个Activity所在的Task中.

  2. singleTop模式的Activity, 仅当该Activity已经在Task的顶部了, 才会复用. 复用时onPause, 然后onNewIntent唤起, 走onResume流程. 否则都要创建新的实例, 放进Task中.

  3. singleTask模式的Activity, 同一个Task中只会存在一个实例. 如果Task中还没有, 则新建, 放在Task顶部; 如果Task中已经有该Activity实例, 则复用.

  4. singleTask模式的Activity的复用模式:

    • 如果已经在Task顶部, 如同singleTop的复用模式;
    • 如果不在Task顶部, 则销毁Task中该Activity顶部的所有其他Activity, 通过onNewIntent唤起该Activity, 走onRestart流程.
  5. singleInstance模式的Activity, 会运行在一个单独的Task中, 且整个系统中只有一个该Activity实例. 相当于单例模式. 复用模式和singleTask一样.

  6. 回答文首提出的Task和Back Stack的关系:

    • 系统中会存在多个Task, 多个Back Stack.
    • 其中一个Back Stack中可以有多个Task.
    • Task可以理解为一次交互的Activities的组合(一般来说一个Application的所有Activity运行在一个Task).
    • Back Stack可以理解为从Launcher界面进入某一个应用开始交互, 可能有很多操作, 这些操作可能分成不同任务的, 例如在编辑联系人的时候跳转到相册了, 可能新启了一个Task, 但是这整个交互流程都在一个Back Stack.
    • 简单来说, 个人理解, Back Stack的重点是在Back, 就是说同一Stack的Activity是可以一直返回的.
  7. 多次实验, 大家也看到了onPause的重要性, 各种流程, onPause都是必不可少的. 这也给了我们App处理的很多启示. 例如重要数据的保存, 另外, 如生命周期一文中所说, 也反映了别的Activity启动是需要等到上一个Activity onPause执行完毕的.

你可能感兴趣的:(探索Activity之launchMode)