Android 的singleTask和singleInstance的一点思考

目录

导语

一、几个概念

1、概念区分

2、 android:taskAffinity

二、详细描述下这四种启动模式

三、singleTask简单分析

1、实例

2、验证singleTask的几个特点

3、简单总结singleTask的特点

四、singleInstance的简单分析

1、验证singleInstance的几个特点

2、简单总结singleInstance的几个特点

五、总结


导语

Activity的四种启动模式主要有standard、singleTop、singleTask、singleInstance四种。不同的启动模式对该Activity有着不同的启动方式,对应AndroidManifest中的android:launchMode属性。

在刚接触这几个概念的时候,就简单的知道

启动模式 简单的最初理解
standard Activity被实例化多次
singleTop 如果在栈顶存在Activitiy实例的话就重用,否则重新创建Activitiy实例
singleTask 如果只要在栈中存在Activitiy实例的话就重用,否则重新创建Activitiy实例
singleInstance 全局只有一个activity实例

 但是随着最近对于源码和有些知识点的研究,有些疑问就来了:

像什么是栈中和栈顶存在实例?这个栈中和栈顶到底是什么意思?

像singleInstance对应的这个实例,这个全局到底指的范围有多广?到底指的是哪个范围内全局?

为什么standard的Activity启动多次,会在按back键的时候,需要一层一层的退出?

。。。。。

有了这些疑问,我就想着一定要研究一下。

一、几个概念

1、概念区分

概念 简单解释
任务(Task)

就是一组Activity的集合。以栈的形式管理开启的activity,开启和关闭activity其实就是在执行压栈和出栈操作。

当不管从Launcher或者其他地方启动应用的时候,会启动应用默认的Activity,这时就会创建或者复用一个任务。默认的之后开启的Activity其实都是在这一个任务中的。
一个Activitiy必定在一个任务中进行创建或复用,而一个任务中可以有多个Activity,当然一个任务中也可以有且仅有一个Activitiy。 

进程(Process) 系统进行资源分配和调度的一个独立单位。不只是程序的代码,还包括当前的活动。打开一个应用,其实就是开启了一个进程,默认情况下,统一应用的所有组件都是在相同的进程中运行的

2、 android:taskAffinity

对于Activity所在的任务其实在AndroidManifest对应的android:taskAffinity属性值。

android:taskAffinity表示Activity所在的任务,默认就是包名,若为空字符串,则表示Activity不属于任何Task;相同的taskAffinity的Activity则在同一个任务中。

二、详细描述下这四种启动模式

启动模式 在详细点
standard 默认的启动模式。每次启动的Activity都会在任务栈中实例化,在该Activity会在所在的任务栈(这个也就是上面我的第一个疑问:这个栈到底指的是什么:我觉得就可以理解为这个Activity所在的这个任务,即一组Activity的集合,该集合只不过是通过栈进行管理)中存在多个Activity的实例,当返回的时候,需要每个Activity分别出栈(也就解释了为什么standard的Activity启动多次,会在按back键的时候,需要一层一层的退出)。
singleTop 如果在任务栈顶存在Activitiy实例,则通过onNewIntent激活重用;只要不在栈顶存在,则创建Activitiy实例,任务栈中会有多个Activity实例。
singleTask

默认的情况下,如果在任务栈中若不存在Activity实例,创建实例;否则则通onNewIntent激活重用,在重用该实例的时候,会将该实例上的其他activity的实例清除。

在对应的任务栈中有且仅有一个实例。

当然如果和android:taskAffinity配合使用,则可以在开启或者复用另外任务栈中来创建或重用Activity实例。

有该Activity启动的其他Activity默认的都会在该Activity所在的任务栈中,除非去设置了android:taskAffinity或将Activity 的launchMode设置为singleInstance。

singleInstance

在新的任务栈中开启,并且该新的任务中有且仅有这一个Activity实例,若复用Activity实例时,则通过onNewIntent进行激活。

有该Activity启动的其他Activity不会在该Activity所在的任务栈中,可以在已有的任务栈中,也可以在新创建的任务中。

并且该Activity实例是在整个系统中有且仅有一个(就是我疑问的这个全局范围有多广)

standard和singleTop这两种方式,其实很简单,就是在任务栈中根据不同的情况多次实例化Activity。重点分析下singleTask和singleInstance 

三、singleTask简单分析

1、实例

应用A:默认启动的Activity为MainActivity,MainActivity来启动launchMode="singleTask"和不设置android:taskAffinity的FirstActivity

通过getTaskId()来查看MainActivity和FirstActivity的任务id,发现一致

01-15 15:02:22.326 4128-4128/com.j1.task D/TAG: MainActivity get task id = 1000
01-15 15:02:24.077 4128-4128/com.j1.task D/TAG: FirstActivity get task id = 1000

通过adb shell dumpsys activity查看,MainActivity和FirstActivity也的确都是在一个任务栈中。

 TaskRecord{af3799f #1000 A=com.j1.task U=0 StackId=1 sz=2}
.......
    Hist #1: ActivityRecord{e764b24 u0 com.j1.task/.FirstActivity t1000}
      Intent { flg=0x10000000 cmp=com.j1.task/.FirstActivity }
      ProcessRecord{8b36aec 4128:com.j1.task/u0a87}
    Hist #0: ActivityRecord{45274f5 u0 com.j1.task/.MainActivity t1000}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.j1.task/.MainActivity bnds=[230,1238][437,1516] }
      ProcessRecord{8b36aec 4128:com.j1.task/u0a87}

这里就有一个疑问

在Google的API文档中说的是:

The system creates a new task and instantiates the activity at the root of the new task. 
系统会创建一个新的任务,初始化该Activity实例,将该实例放到新任务栈的栈底。

和刚才输出的结果是相矛盾的。

实际上,在singleTask模式下,系统在启动该Activity的时候,还会受android:taskAffinity这个属性限制。

其实现过程如下:

1)系统在启动singleTask的FirstActivity的时候,会标记为FLAG_ACTIVITY_NEW_TASK

2)检测android:taskAffinity这个属性所在的任务是否存在,若不存在,则新建该任务栈,如果存在该任务栈,则调度到前台

3)在任务栈中查找是否存在FirstActivity实例,如果不存在,则创建FirstActivity实例;若存在,则通过onNewIntent激活。

由于FirstActivity没有设置android:taskAffinity,所以默认的为包名,则就是MainActivity所在的任务栈,所以上面的两个task id为相同的。当设置不同的android:taskAffinity的时,就可以创建新的任务栈了。下面也会通过具体的实例来验证singleTask的这些特点。

2、验证singleTask的几个特点

1)若存在实例,则重用该实例,并会将该任务栈上面的其他Activity实例清除。

继续上面的实例,在FirstActivity上在启动没有设置aunchMode和taskAffinity的SecondActivity

通过getTaskId()来查看三个Activity的task id

01-15 15:24:31.620 5037-5037/? D/TAG: MainActivity get task id = 1005
01-15 15:24:39.860 5037-5037/com.j1.task D/TAG: FirstActivity get task id = 1005
01-15 15:24:45.870 5037-5037/com.j1.task D/TAG: SecondActivity get task id = 1005

通过adb shell dumpsys activity查看,三个Activity在同一个任务栈中。

TaskRecord{2746d98 #1005 A=com.j1.task U=0 StackId=1 sz=3}
     .........
    Hist #2: ActivityRecord{b429705 u0 com.j1.task/.SecondActivity t1005}
       Intent { cmp=com.j1.task/.SecondActivity }
       ProcessRecord{8729f1 5176:com.j1.task/u0a87}
    Hist #1: ActivityRecord{dabde95 u0 com.j1.task/.FirstActivity t1005}
       Intent { flg=0x10000000 cmp=com.j1.task/.FirstActivity }
       ProcessRecord{8729f1 5176:com.j1.task/u0a87}
    Hist #0: ActivityRecord{9a270ad u0 com.j1.task/.MainActivity t1005}
       Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.j1.task/.MainActivity }
      ProcessRecord{8729f1 5176:com.j1.task/u0a87}

此时任务栈中依次为: MainActivity(9a270ad)、FirstActivity(dabde95)、SecondActivity(b429705)

现在在SecondActivity中开启FirstActivity,在通过adb shell dumpsys activity查看,发现任务栈中只有 MainActivity(9a270ad)、FirstActivity(dabde95)

TaskRecord{2746d98 #1005 A=com.j1.task U=0 StackId=1 sz=2}
    .......
   Hist #1: ActivityRecord{dabde95 u0 com.j1.task/.FirstActivity t1005}
      Intent { flg=0x10000000 cmp=com.j1.task/.FirstActivity }
      ProcessRecord{8729f1 5176:com.j1.task/u0a87}
   Hist #0: ActivityRecord{9a270ad u0 com.j1.task/.MainActivity t1005}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.j1.task/.MainActivity }
      ProcessRecord{8729f1 5176:com.j1.task/u0a87}

我们对比两个FirstActivity红色标记的对应的是同一个实例,同时SecondActivity已经被自动清空了。

2)singleTask和android:taskAffinity巧妙的配合使用

  • (1)开启新的任务栈

实例:默认启动的Activity为MainActivity,MainActivity来启动launchMode="singleTask"和android:taskAffinity="com.j1.task2"的FirstActivity

还是通过getTaskId()来查看task id,发现两个的任务Id已经不相同

01-15 15:43:29.142 5825-5825/? D/TAG: MainActivity get task id = 1006
01-15 15:43:33.720 5825-5825/com.j1.task D/TAG: FirstActivity get task id = 1007

同样通过adb shell dumpsys activity查看

TaskRecord{5f8ce8c #1007 A=com.j1.task2 U=0 StackId=1 sz=1}
    Run #1: ActivityRecord{9a791e9 u0 com.j1.task/.FirstActivity t1007}
TaskRecord{626c9d5 #1006 A=com.j1.task U=0 StackId=1 sz=1}
    Run #0: ActivityRecord{778236f u0 com.j1.task/.MainActivity t1006}

同样发现这里也新建了一个任务。所以当设置 android:taskAffinity的时候,可以在新的任务栈中创建Activity实例

  • (2)singleTask的Activity去启动其他Activity,其他Activity会在singleTask的Activity所在的任务栈

接着上面的实例,用FirstActivity去开启SecondActivity

通过getTaskId()发现SecondActivity 和FirstActivity的task id相同,和MainActivity的task id已经不在相同

01-15 15:43:29.142 5825-5825/? D/TAG: MainActivity get task id = 1006
01-15 15:43:33.720 5825-5825/com.j1.task D/TAG: FirstActivity get task id = 1007
01-15 16:10:48.459 5825-5825/com.j1.task D/TAG: SecondActivity get task id = 1007

同样通过adb shell dumpsys activity查看

TaskRecord{5f8ce8c #1007 A=com.j1.task2 U=0 StackId=1 sz=2}
    Run #2: ActivityRecord{6819135 u0 com.j1.task/.SecondActivity t1007}
    Run #1: ActivityRecord{9a791e9 u0 com.j1.task/.FirstActivity t1007}
TaskRecord{626c9d5 #1006 A=com.j1.task U=0 StackId=1 sz=1}
    Run #0: ActivityRecord{778236f u0 com.j1.task/.MainActivity t1006}

发现由FirstActivity开启的SecondActivity也是在FirstActivity的任务栈中了 。

  • (3)复用已存在的任务栈

主要用在不同的应用之间可以将不同的Activity设置为相同的android:taskAffinity。

还是紧接上面的实例,增加第二个应用:该应用中默认启动的为MainActivity1,在MainActivity1中去启动设置launchMode="singleTask"和android:taskAffinity="com.j1.task2"的FirstActivity1。

我们先将之前的应用置于后台,打开第二个应用的MainActivity1,然后打开FirstActivity1。

通过getTaskId()发现,FirstActivity1的task id和第一个应用中的FirstActivity是一致的,但和MainActivity1的task id不一致

01-15 16:18:56.792 6890-6890/? D/TAG: MainActivity1 get task id = 1008
01-15 16:21:36.266 6890-6890/com.j1.task3 D/TAG: FirstActivity1 get task id = 1007

同样通过adb shell dumpsys activity查看

TaskRecord{5f8ce8c #1007 A=com.j1.task2 U=0 StackId=1 sz=3}
     .......
    Hist #2: ActivityRecord{f468078 u0 com.j1.task3/.FirstActivity1 t1007}
       Intent { flg=0x10400000 cmp=com.j1.task3/.FirstActivity1 }
       ProcessRecord{5c85437 6890:com.j1.task3/u0a88}
    Hist #1: ActivityRecord{6819135 u0 com.j1.task/.SecondActivity t1007}
       Intent { cmp=com.j1.task/.SecondActivity }
       ProcessRecord{fc105ea 5825:com.j1.task/u0a87}
    Hist #0: ActivityRecord{9a791e9 u0 com.j1.task/.FirstActivity t1007}
       Intent { flg=0x10000000 cmp=com.j1.task/.FirstActivity }
       ProcessRecord{fc105ea 5825:com.j1.task/u0a87}

发现FirstActivity1会在FirstActivity所在的任务栈中,复用了FirstActivity的任务栈。并且FirstActivity1所在的进程为5c85437,而FirstActivity所在的进程为fc105ea。

而第二个应用的任务栈只有MainActivity1所在的任务栈。

TaskRecord{78e5d36 #1008 A=com.j1.task3 U=0 StackId=1 sz=1}
.....
    Hist #0: ActivityRecord{207eb55 u0 com.j1.task3/.MainActivity1 t1008}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.j1.task3/.MainActivity1 }
      ProcessRecord{5c85437 6890:com.j1.task3/u0a88}

同时在第二个应用的FirstActivity1界面按返回键的时候,返回也是第一个应用的SecondActivity的界面,更加证明了:FirstActivity1在复用FirstActivity创建的任务栈。

  • (4)任务栈中只存在一个activity实例

上面的(1)其实也验证了任务栈总只有一个实例。

3、简单总结singleTask的特点

1)默认情况下,开启singleTask的Activity并不会创建新任务,要配合android:taskAffinity来决定是否创建新任务。

2)在任务栈中先判断是否有Activity实例,若不存在,则直接在任务栈中创建Activity实例;若存在,则从任务栈中通过onNewIntent()激活该Activity实例,并将该实例上面的其他Activity实例给清空。

3)可以通过android:taskAffinity在另外一个应用中复用任务栈。

4)在Activity实例所在的任务栈中,该实例有且仅有一个。

四、singleInstance的简单分析

1、验证singleInstance的几个特点

1)该Activity在新任务中开启,并且该任务有且仅有该Activity实例

实例:默认启动的Activity为MainActivity,MainActivity来启动launchMode="singleInstance"和没有设置android:taskAffinity的FirstActivity,同时FirstActivity去开启没有设置launchMode和android:taskAffinity的SecondActivity

通过getTaskId()发现FirstActivity和MainActivity的taskId不一致,并且有FirstActivity开启的SecondActivity也不和FirstActivity的task Id不一致

01-15 16:58:37.714 8107-8107/com.j1.task D/TAG: MainActivity get task id = 1012
01-15 16:58:40.970 8107-8107/com.j1.task D/TAG: FirstActivity get task id = 1013
01-15 16:58:50.774 8107-8107/com.j1.task D/TAG: SecondActivity get task id = 1012

同样通过adb shell dumpsys activity查看

TaskRecord{67aec12 #1012 A=com.j1.task U=0 StackId=1 sz=2}
   ......
   Hist #1: ActivityRecord{58328d0 u0 com.j1.task/.SecondActivity t1012}
     Intent { flg=0x10400000 cmp=com.j1.task/.SecondActivity }
     ProcessRecord{5adbde0 8107:com.j1.task/u0a87}
   Hist #0: ActivityRecord{ed15ad1 u0 com.j1.task/.MainActivity t1012}
      Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.j1.task/.MainActivity bnds=[643,245][849,523] }
      ProcessRecord{5adbde0 8107:com.j1.task/u0a87}

TaskRecord{6144899 #1013 A=com.j1.task U=0 StackId=1 sz=1}
   ......
  Hist #0: ActivityRecord{c4d910f u0 com.j1.task/.FirstActivity t1013}
     Intent { flg=0x10000000 cmp=com.j1.task/.FirstActivity }
     ProcessRecord{5adbde0 8107:com.j1.task/u0a87}

可以看到FirstActivity会重新创建一个新的任务栈,并且有它开启的SecondActivity也不会在该任务栈中创建。 

这里的有且仅有还要在验证下。假设我们将FirstActivity的android:taskAffinity设置为"com.j1.task4",SecondActivity的launchMode="singleTask"和android:taskAffinity设置为"com.j1.task4"。

通过查看task id 发现,FirstActivity和SecondActivity的taskid是不一致的

01-16 14:30:48.932 11643-11643/? D/TAG: MainActivity get task id = 1057
01-16 14:31:03.215 11643-11643/com.j1.task D/TAG: FirstActivity get task id = 1059
01-16 14:31:08.614 11643-11643/com.j1.task D/TAG: SecondActivity get task id = 1058

同样通过adb shell dumpsys activity查看 ,虽然名字都是"com.j1.task4",但是仍然是不同的任务栈。

TaskRecord{236bd7a #1058 A=com.j1.task4 U=0 StackId=1 sz=1}
    Run #2: ActivityRecord{1f3d56f u0 com.j1.task/.SecondActivity t1058}
TaskRecord{f6fb721 #1059 A=com.j1.task4 U=0 StackId=1 sz=1}
     Run #1: ActivityRecord{8e51ef3 u0 com.j1.task/.FirstActivity t1059}
TaskRecord{566092b #1057 A=com.j1.task U=0 StackId=1 sz=1}
    Run #0: ActivityRecord{53f99eb u0 com.j1.task/.MainActivity t1057}

 所以singleInstance的Activity所在的任务栈中有且仅有该Activity实例一个

2)系统全局只有一个Activity实例

实例:给上述的提到的应用的FirstActivity,增加一个intentFilter,在第二个应用中通过隐式打开FirstActivity

第一个APP仍然直到打开SecondActivity,查看getTaskId()

01-15 17:09:10.591 8702-8702/? D/TAG: MainActivity get task id = 1014
01-15 17:09:48.447 8702-8702/com.j1.task D/TAG: FirstActivity get task id = 1015
01-15 17:10:05.749 8702-8702/com.j1.task D/TAG: SecondActivity get task id = 1014

同样通过adb shell dumpsys activity查看

TaskRecord{ba4968f #1014 A=com.j1.task U=0 StackId=1 sz=2}
      .....
    Hist #1: ActivityRecord{5f6eaa7 u0 com.j1.task/.SecondActivity t1014}
       Intent { flg=0x10400000 cmp=com.j1.task/.SecondActivity }
       ProcessRecord{c8ddd1c 8702:com.j1.task/u0a87}
    Hist #0: ActivityRecord{d2fad2b u0 com.j1.task/.MainActivity t1014}
        Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.j1.task/.MainActivity }
        ProcessRecord{c8ddd1c 8702:com.j1.task/u0a87}

TaskRecord{16ddd25 #1015 A=com.j1.task U=0 StackId=1 sz=1}
     ......
    Hist #0: ActivityRecord{7708492 u0 com.j1.task/.FirstActivity t1015}
       Intent { flg=0x10000000 cmp=com.j1.task/.FirstActivity }
       ProcessRecord{c8ddd1c 8702:com.j1.task/u0a87}

此时的 FirstActivity的AcitivityRecord为7708492,t1015  。 

打开第二个应用,在MainActivity通过隐式打开FirstActivity

查看getTaskId(),第二个应用的只有该输出

01-15 17:13:52.167 8897-8897/com.j1.task3 D/TAG: MainActivity get task id = 1016 

同样通过adb shell dumpsys activity查看

 TaskRecord{16ddd25 #1015 A=com.j1.task U=0 StackId=1 sz=1}
    Run #3: ActivityRecord{7708492 u0 com.j1.task/.FirstActivity t1015}
TaskRecord{cbd269a #1016 A=com.j1.task3 U=0 StackId=1 sz=1}
    Run #2: ActivityRecord{c3ca76e u0 com.j1.task3/.MainActivity1 t1016}
 TaskRecord{ba4968f #1014 A=com.j1.task U=0 StackId=1 sz=2}
     Run #1: ActivityRecord{5f6eaa7 u0 com.j1.task/.SecondActivity t1014}
     Run #0: ActivityRecord{d2fad2b u0 com.j1.task/.MainActivity t1014}

此时FirstActivity的AcitivityRecord仍为7708492,任务栈id为1015  。这说明singleInstance的activity实例整个系统中有且仅有一个。

3)singleInstance的activity在启动其他Activity时,其他Activity可以新创建一个任务栈,也可以在已有的任务栈中。

上述的第一个例子中可以看出,如果被singleInstance的activity开启的Activity,如果不设置android:taskAffinity,则会在默认的任务栈中创建实例。

紧接着1)提到的例子来继续,将SecondActivity设置成android:taskAffinity="com.j1.task2"。

通过getTaskId()发现,和1)不同的是,此时SecondActivity已经和MainActivity的task id不在相同。

01-16 10:22:54.527 3904-3904/com.j1.task D/TAG: MainActivity get task id = 1030
01-16 10:22:56.602 3904-3904/com.j1.task D/TAG: FirstActivity get task id = 1031
01-16 10:23:01.294 3904-3904/com.j1.task D/TAG: SecondActivity get task id = 1032

同样通过adb shell dumpsys activity查看

TaskRecord{faddb55 #1032 A=com.j1.task2 U=0 StackId=1 sz=1}
    Run #2: ActivityRecord{221ce9d u0 com.j1.task/.SecondActivity t1032}
TaskRecord{e98796a #1031 A=com.j1.task U=0 StackId=1 sz=1}
    Run #1: ActivityRecord{87bb114 u0 com.j1.task/.FirstActivity t1031}
TaskRecord{935741 #1030 A=com.j1.task U=0 StackId=1 sz=1}
    Run #0: ActivityRecord{c2b521c u0 com.j1.task/.MainActivity t1030}

也发现SecondActivity已经在一个新的任务栈中了。不需要设置launchMode就可以创建一个com.j1.task2的新任务栈

那为什么会这样子呢?

1)singleInstance的FirstActivity在开启SecondActivity的时候,系统会给SecondActivity添加FLAG_ACTIVITY_NEW_TASK的标记。

2)检测SecondActivity对应的android:taskAffinity是否存在,默认的为包名,但一定不是singleInstance的FirstActivity的任务栈。若该任务栈已经存在,则在已有的任务栈中查找SecondActivity的实例;若任务栈不存在,则创建新的任务栈。该包名的任务栈是有MainActivity创建的,所以此时任务栈已经存在。

3)有了任务栈之后,是怎么创建SecondActivity实例呢?

  • (1)SecondActivity并没有设置android:launchMode,所以直接新建SecondActivity实例。

增加一个实例来验证下。

实例:默认启动的Activity为MainActivity,MainActivity来启动没有设置android:launchMode和android:taskAffinity的SecondActivity,SecondActivity去启动launchMode="singleInstance"和没有设置android:taskAffinity的FirstActivity,在FirstActivity中再去启动SecondActivity。

第一次打开SecondActivity,查看task id

01-16 10:51:05.164 5279-5279/com.j1.task D/TAG: MainActivity get task id = 1045
01-16 10:51:17.459 5279-5279/com.j1.task D/TAG: SecondActivity get task id = 1045

因为没有设置SecondActivity的android:tlaunchMode和android:taskAffinity,所以和MainActivity的task id一致。

同样通过adb shell dumpsys activity查看

TaskRecord{bae1c1b #1045 A=com.j1.task U=0 StackId=1 sz=2}
   Run #1: ActivityRecord{205052d u0 com.j1.task/.SecondActivity t1045}
   Run #0: ActivityRecord{64deeec u0 com.j1.task/.MainActivity t1045}

在SecondActivity中打开singleInstance的 FirstActivity,查看task id,发现FirstActivity也的确重新在新的任务栈中开启

01-16 11:36:33.567 5279-5279/com.j1.task D/TAG: FirstActivity get task id = 1046

同样通过adb shell dumpsys activity查看

TaskRecord{a43db91 #1046 A=com.j1.task U=0 StackId=1 sz=1}
   Run #2: ActivityRecord{a9f3186 u0 com.j1.task/.FirstActivity t1046}
TaskRecord{bae1c1b #1045 A=com.j1.task U=0 StackId=1 sz=2}
   Run #1: ActivityRecord{205052d u0 com.j1.task/.SecondActivity t1045}
   Run #0: ActivityRecord{64deeec u0 com.j1.task/.MainActivity t1045}

在FirstActivity中再一次打开SecondActivity,查看task id

01-16 11:38:36.009 5279-5279/com.j1.task D/TAG: SecondActivity get task id = 1045

同样通过adb shell dumpsys activity查看,第二个SecondActivity的ActivityRecord为8c7771a ,第一个SecondActivity的ActivityRecord为205052d,也已经不是同一个实例了

TaskRecord{bae1c1b #1045 A=com.j1.task U=0 StackId=1 sz=3}
    Run #3: ActivityRecord{8c7771a u0 com.j1.task/.SecondActivity t1045}
TaskRecord{a43db91 #1046 A=com.j1.task U=0 StackId=1 sz=1}
    Run #2: ActivityRecord{a9f3186 u0 com.j1.task/.FirstActivity t1046}
TaskRecord{bae1c1b #1045 A=com.j1.task U=0 StackId=1 sz=3}
   Run #1: ActivityRecord{205052d u0 com.j1.task/.SecondActivity t1045}
   Run #0: ActivityRecord{64deeec u0 com.j1.task/.MainActivity t1045}

在这个之后,按返回键,此时也只是将1045这个任务栈的Activity依次弹出。

 

满足下我的好奇心,假设把SecondActivity放到另外一个任务栈中呢?

  • (2)有了任务栈之后,SecondActivity的launchMode为singleTask

同样上面的例子,将SecondActivity的launchMode设置为singleTask,android:taskAffinity设置为com.j1.task4。

第一次MainActivity打开SecondActivity,查看task id,SecondActivity和MainActivity的task id是不同的

01-16 13:57:23.292 10618-10618/com.j1.task D/TAG: MainActivity get task id = 1054
01-16 13:57:24.871 10618-10618/com.j1.task D/TAG: SecondActivity get task id = 1055

同样通过adb shell dumpsys activity查看,

TaskRecord{e1af5d3 #1055 A=com.j1.task4 U=0 StackId=1 sz=1}
   Run #1: ActivityRecord{25d81f3 u0 com.j1.task/.SecondActivity t1055}
TaskRecord{c932510 #1054 A=com.j1.task U=0 StackId=1 sz=1}
   Run #0: ActivityRecord{739b995 u0 com.j1.task/.MainActivity t1054}

第二次通过FirstActivity打开SecondActivity,查看task id,

01-16 13:58:19.805 10618-10618/com.j1.task D/TAG: FirstActivity get task id = 1056
01-16 13:58:21.777 10618-10618/com.j1.task D/TAG: SecondActivity onNewIntent get task id = 1055 

此时SecondActivity已经复用了,不在重新创建,任务id 仍为之前的id

同样通过adb shell dumpsys activity查看,此时SecondActivity的ActivityRecord仍为25d81f3

TaskRecord{e1af5d3 #1055 A=com.j1.task4 U=0 StackId=1 sz=1}
    Run #2: ActivityRecord{25d81f3 u0 com.j1.task/.SecondActivity t1055}
TaskRecord{261bc0b #1056 A=com.j1.task U=0 StackId=1 sz=1}
    Run #1: ActivityRecord{da3ce7d u0 com.j1.task/.FirstActivity t1056}
TaskRecord{c932510 #1054 A=com.j1.task U=0 StackId=1 sz=1}
    Run #0: ActivityRecord{739b995 u0 com.j1.task/.MainActivity t1054}

综述(1)和(2),其实有 singleInstance的Activity的启动其他Activity,除去一定不会在 singleInstance的Activity所在的任务创建实例之外,所在的任务栈和是否创建实例其实仍然取决于launchMode和android:taskAffinity。

2、简单总结singleInstance的几个特点

1)独占一个任务栈,该任务栈中有且仅有该Activity实例

2)整个系统就只有一个实例。

3)被singleIntance的Activity启动的其他Activity,默认的在包名的任务栈中,如果配合android:taskAffinity,也可以新的任务栈中创建的实例。

4)被singleIntance的Activity启动的其他Activity,一定不在singleInstance的Activity所在的栈中,其他Activity的实例的创建和使用取决于该Activity设置的launchMode和android:taskAffinity

五、总结

经过这些分析之后,感觉自己之前有些模棱两可的知识点逐渐清晰起来。系统通过栈的形式管理了一系列的Activity的集合,也就是我们所说的任务栈。

1)当启动模式为standard和singleTop的时候,系统只会在同一任务中对Activity进行创建或复用;

2)当启动模式为singleTask的时候,系统首先会检测该Activity对应的android:taskAffinity任务栈是否存在,若存在,则将该任务切换到前台重用该任务,然后在该任务中查找实例;否则重新创建任务

3)当启动模式为singleInstance的时候,系统首先会检测该Activity实例是否存在,若存在,则将相应的任务切换到前台,重用该实例,否则创建新任务,在新的任务中创建实例。

另外我们平时按home键,也就是将前台任务切换到后台,并且在有些手机上长按home键出现的近期的任务列表,当我们点击的时候,仍然可以将该任务切换到前台。所以在打开一个应用的时候,其实就是创建任务或者把之前的任务切换到前台的一个过程。

另外里面分析的不对的地方,也恳请大家指出来。

 

你可能感兴趣的:(android基本知识)