本文出自 “阿敏其人” 简书博客,转载或引用请注明出处。
Android-Activity所应该了解的大概就这样。(上)
Android-Activity所应该了解的大概就这样。(下)
五、任务栈/Activity的启动模式
知道生命周期、线程的优先级和Activity的异常销毁,下面我们来认识一下任务栈。
我们上面进行的那么多描述和代码,都是在standard这种默认的任务栈进行的。
栈的概念这里就不再赘述,这里知道先进后出就好。就像子弹夹。
不同的任务栈,也称为不同的启动模式。
1、任务栈的分类:
任务栈有以下四种
- ** standard 默认的启动模式,标准模式**
- ** singletop 单一顶部模式 (顶部不会重复)**
- ** singleTask 单一任务栈,干掉头上的其他Activity**
- ** singleInstance 单一实例(单例),任务栈里面自已自己一个人**
一般来说用默认的就好,当我们程序感觉切换奇怪,或者某个activity占用开销太大之类的,才考虑使用其他的启动默认。
2、指定任务栈Activity的启动模式
待会我们再来详细解释这些不同的任务栈详细区别,现在,我们先看一下怎么指定一个Activity的任务栈模式,也就是启动模式。
默认的Activity都是standard模式的,那如果我们要把一个Activity指定为 singleTask 模式呢?
有两种启动方法:一种方法是manifest指定,另外一种方式是代码指定。
2.1 manifest指定
比如我们要指定为singleTask模式
manifest里面的Activity有个 launchMode属性来制定启动模式:
<activity android:name=".SecondActivity"
android:launchMode="singleTask"
/>
2.2 代码指定 intent.addFlag
比如我们要指定为singleTop模式
Intent intent = new Intent();
intent.setClass(FirstActivity.this,SecondActivity.class);
// 通过Intent的addFlag指定
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
2.3、两种方式的对比
1、从优先级来说,代码指定优先于manifest指定
2、两者各有局限,
manifest无法设定 FLAG_ACTIVITY_CLEAR_TOP 标识
代码指定无法指定为 singleInstance启动模式
3、怎么查看当前app的任务栈数量和任务栈里面的Activity
3.1 adb shell dumpsys activity
在终端键入这样的指令:
adb shell dumpsys activity
查看当前手机的任务栈运行情况
即可得到我们想要的信息,回车后会列出相当多的信息,我们需要找到如下的分类
ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
下面是一份示例log摘取:
ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
Stack #0:
Task id #1
TaskRecord{529322d8 #1 A=com.android.launcher U=0 sz=1}
Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10600000 cmp=com.android.launcher/com.android.launcher2.Launcher }
Hist #0: ActivityRecord{5297586c u0 com.android.launcher/com.android.launcher2.Launcher t1}
Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000000 cmp=com.android.launcher/com.android.launcher2.Launcher }
ProcessRecord{5293299c 17717:com.android.launcher/u0a8}
Running activities (most recent first):
TaskRecord{529322d8 #1 A=com.android.launcher U=0 sz=1}
Run #0: ActivityRecord{5297586c u0 com.android.launcher/com.android.launcher2.Launcher t1}
Stack #1:
Task id #25
TaskRecord{52a1682c #25 A=com.amqr.taskstack U=0 sz=3}
Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.amqr.taskstack/.FirstActivity }
Hist #2: ActivityRecord{52980338 u0 com.amqr.taskstack/.ThirdActivity t25}
Intent { cmp=com.amqr.taskstack/.ThirdActivity }
ProcessRecord{5296870c 32225:com.amqr.taskstack/u0a85}
Hist #1: ActivityRecord{5297ef0c u0 com.amqr.taskstack/.SecondActivity t25}
Intent { flg=0x20000000 cmp=com.amqr.taskstack/.SecondActivity }
ProcessRecord{5296870c 32225:com.amqr.taskstack/u0a85}
Hist #0: ActivityRecord{529f1cc8 u0 com.amqr.taskstack/.FirstActivity t25}
Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.amqr.taskstack/.FirstActivity }
ProcessRecord{5296870c 32225:com.amqr.taskstack/u0a85}
Running activities (most recent first):
TaskRecord{52a1682c #25 A=com.amqr.taskstack U=0 sz=3}
Run #2: ActivityRecord{52980338 u0 com.amqr.taskstack/.ThirdActivity t25}
Run #1: ActivityRecord{5297ef0c u0 com.amqr.taskstack/.SecondActivity t25}
Run #0: ActivityRecord{529f1cc8 u0 com.amqr.taskstack/.FirstActivity t25}
mResumedActivity: ActivityRecord{52980338 u0 com.amqr.taskstack/.ThirdActivity t25}
mFocusedActivity: ActivityRecord{52980338 u0 com.amqr.taskstack/.ThirdActivity t25}
mDismissKeyguardOnNextActivity=false
mFocusedStack=ActivityStack{52a267fc stackId=10, 1 tasks} mStackState=STACK_STATE_HOME_IN_BACK
mSleepTimeout=false
mCurTaskId=25
mUserStackInFront={}
Recent tasks:
* Recent #0: TaskRecord{52a1682c #25 A=com.amqr.taskstack U=0 sz=3}
* Recent #1: TaskRecord{529322d8 #1 A=com.android.launcher U=0 sz=1}
* Recent #2: TaskRecord{529890a0 #11 A=com.android.systemui U=0 sz=0}
3.2、 ACTIVITY MANAGER ACTIVITIES
在ACTIVITY MANAGER ACTIVITIES 大分类里,
找到Running activities (most recent first),可以看到最近正在操作的那个程序涉及到任务栈个数,以及每个任务栈的Activity数量,根据位置上下排序,最上面的是栈顶,最下面的是栈底。栈顶就是最近的操作界面
3.3、Running activities (most recent first)
我们把Running activities (most recent first)单独拿出来说。
下面这份信息,显示着最近运行的一个程序的只有一个任务栈,任务栈里面有三个activit,栈顶是ThirdActivity,栈底是FirstActivity。
这个任务栈的名称是com.amqr.taskstack。
Running activities (most recent first):
TaskRecord{52a1682c #25 A=com.amqr.taskstack U=0 sz=3}
Run #2: ActivityRecord{52980338 u0 com.amqr.taskstack/.ThirdActivity t25}
Run #1: ActivityRecord{5297ef0c u0 com.amqr.taskstack/.SecondActivity t25}
Run #0: ActivityRecord{529f1cc8 u0 com.amqr.taskstack/.FirstActivity t25}
再来一份例子:
Running activities (most recent first):
TaskRecord{528f99b4 #12 A=com.taskstack.thirdtask U=0 sz=1}
Run #1: ActivityRecord{529d828c u0 com.amqr.taskstack/.ThirdActivity t12}
TaskRecord{529e4584 #11 A=com.amqr.taskstack U=0 sz=1}
Run #0: ActivityRecord{529a8c5c u0 com.amqr.taskstack/.FirstActivity t11}
比如这份终端信息。就表示当前当前最近的运行的有两个任务栈的,(为什么一个程序的运行有两个任务栈?这就涉及到singleTask或者singleInstance启动模式了,后面会细说。)
其中,一个任务栈的是com.taskstack.thirdtask,(我们自己指定的名称),当前这个任务栈里面只有一个activity。
另外一个任务栈的名称是com.amqr.taskstack,这个是系统默认自带的任务栈,名字就是包名。
我们拿出一条来分析把
TaskRecord{528f99b4 #12 A=com.taskstack.thirdtask U=0 sz=1}
Run #1: ActivityRecord{529d828c u0 com.amqr.taskstack/.ThirdActivity t12}
像这个,528f99b4 #12这么一串里面,这个#12可以说是这个任务栈的唯一标示(有时候会两个任务栈出现 任务栈A=“aaa.bbb.ccc”的aaa.bbb.ccc一样的情况,想要根本区分�是否为同一个任务栈,用的就是这个 #号+num,如果两个人任务栈的 #+num 和 A="aaa.bbb.ccc" 两者都一致的话,那么这两个任务栈绝对会同一个任务栈)
至于最后面的 sz=num,这个num代表这个任务栈里面当前存放了多少个Activity。
什么时候出现两个任务栈 aaa..bbb.ccc 相同的但是 #num 不同:singleInstance
什么时候出现两个任务栈 aaa.bbb.ccc 和 #num 都相同:singleTask + taskAffinity
3.4、 Recent tasks 手机当前的运行的任务栈
(注意:Recent tasks代表的最近手机运行的程序的任务栈,不是对应正在运行的程序的个数(因为有的程序可能有多个任务栈),更加不是进程数。)
Recent tasks:
* Recent #0: TaskRecord{52a1682c #25 A=com.amqr.taskstack U=0 sz=3}
* Recent #1: TaskRecord{529322d8 #1 A=com.android.launcher U=0 sz=1}
* Recent #2: TaskRecord{529890a0 #11 A=com.android.systemui U=0 sz=0}
这个是记录手机当前的运行的任务栈数量的。
当我们长按home键,或者按下虚拟的菜单键,(因机型而异),反正就是列出正在运行的运行的任务栈的界面,假设我们的正在运行的是1个任务栈,一般来说,这时Rcent tasks就会显示记录着3个任务栈,如果我们手机显示正在运行两个2个,那么终端的Rcent tasks就会显示记录着4个任务栈,即为N+2个。(有时候会是N+1,只有com.android.launcher )
多出来的那个两个上面写的很清楚,那么就是com.android.launcher 和 com.android.systemui,即为系统启动器和系统UI界面。
我们在来个图文详细点的吧。
当前手机任务列表运行着3个任务栈(不要以为任务栈就是程序,一个程序可能有多个任务栈)
查看一下终端,会显示记录着5个。
Recent tasks:
* Recent #0: TaskRecord{52a0c9e4 #16 A=com.android.systemui U=0 sz=1}
* Recent #1: TaskRecord{528ea064 #1 A=com.android.launcher U=0 sz=1}
* Recent #2: TaskRecord{529e0c18 #18 A=android.task.mms U=0 sz=1}
* Recent #3: TaskRecord{529b3f00 #17 A=android.task.contacts U=0 sz=1}
* Recent #4: TaskRecord{529589e4 #15 A=android.task.browser U=0 sz=1}
.
.
再次说明,列表显示的是任务栈,不是程序
.
.
4、四种启动模式详解
4.1、 standard 默认的启动模式,标准模式
结论:每开启一个Activity,就会在栈顶添加一个Activity实例。多次间隔或者直接启动一个甲Activity会添加多个甲的示例,可重复添加。(间隔 ABA, 直接 ACC或者AAA)
这里我们需要明白一个事情,Service和ApplicationContext是没办法直接开启一个新的Activity,因为只有Activity类型的Context的Activity才能开启,但还是有解决办法的,那就是让我们要开的那个新的Activity设置为FLAG_ACTIVITY_NEW_TASK标识。
情景实测
比如我们的程序里面有FirstActivity,SecondActivity、ThirdActivity和Fourth四个Activity,为了表示方便,我们就用A,B,C和D来分别指代吧。其中,A为启动页。(当前A,B,C,D都是standard模式)
- 情况1: 启动后显示A,接着打开B,紧接着打开C。那么显而易见,这时候只有一个任务栈,假设为S1
启动A:任务栈S1里面只有A
接着打开B:任务栈里面变成BA,B在A上面,B为栈顶。
接着打开C,任务栈里面变成了CBA,栈顶是C,栈底是A。
大概是这个样子
后面不会这么贴图了,第一个就图文说的清楚一些。
- 情况2: 打开A,打开B,再次打开A
此时任务栈: ABA
那么任务栈里面的是 ABA , 其中第一次打开的A位于栈底,第二次打开的A为了栈顶。
利用adb shell dumpsys activity查看Running activities (most recent first)可以得到证实:
Running activities (most recent first):
TaskRecord{52995938 #35 A=com.amqr.taskstack U=0 sz=3}
Run #2: ActivityRecord{52a02754 u0 com.amqr.taskstack/.FirstActivity t35}
Run #1: ActivityRecord{529fdd68 u0 com.amqr.taskstack/.SecondActivity t35}
Run #0: ActivityRecord{529ed754 u0 com.amqr.taskstack/.FirstActivity t35}
- 情况3:打开A,打开C,再打开C
任务栈 CCA,其中第二个C是栈顶,A位于栈底。
Running activities (most recent first):
TaskRecord{529e1bac #4 A=com.amqr.taskstack U=0 sz=3}
Run #2: ActivityRecord{529ae2d8 u0 com.amqr.taskstack/.ThirdActivity t4}
Run #1: ActivityRecord{5299b11c u0 com.amqr.taskstack/.ThirdActivity t4}
Run #0: ActivityRecord{529379d0 u0 com.amqr.taskstack/.FirstActivity t4}
.
顺便附上两次打开C的生命周期方法log,附上这个log是为了跟singleTask的做对比。
12-03 07:15:21.176 15445-15445/com.amqr.taskstack D/Cur: FirstActicity onCreate
12-03 07:15:21.176 15445-15445/com.amqr.taskstack D/Cur: FirstActivity onStart
12-03 07:15:21.176 15445-15445/com.amqr.taskstack D/Cur: FirstActivity onResume
第一次按下打开C
12-03 07:15:23.360 15445-15445/com.amqr.taskstack D/Cur: FirstActivity onPause
12-03 07:15:23.376 15445-15445/com.amqr.taskstack D/Cur: ThirdActivity onCreate
12-03 07:15:23.376 15445-15445/com.amqr.taskstack D/Cur: ThirdActivity onStart
12-03 07:15:23.376 15445-15445/com.amqr.taskstack D/Cur: ThirdActivity onResume
12-03 07:15:23.788 15445-15445/com.amqr.taskstack D/Cur: FirstActivity onStop
第二次按下打开C
12-03 07:15:25.668 15445-15445/com.amqr.taskstack D/Cur: ThirdActivity onPause
12-03 07:15:25.688 15445-15445/com.amqr.taskstack D/Cur: ThirdActivity onCreate
12-03 07:15:25.688 15445-15445/com.amqr.taskstack D/Cur: ThirdActivity onStart
12-03 07:15:25.688 15445-15445/com.amqr.taskstack D/Cur: ThirdActivity onResume
12-03 07:15:26.068 15445-15445/com.amqr.taskstack D/Cur: ThirdActivity onStop
附上FirstActivity的代码,其他三个Activity代码类似。
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
Log.d("Cur", "FirstActicity onCreate");
findViewById(R.id.mBtn1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(FirstActivity.this, FirstActivity.class));
}
});
findViewById(R.id.mBtn2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(FirstActivity.this,SecondActivity.class));
/*Intent intent = new Intent();
intent.setClass(FirstActivity.this,SecondActivity.class);
// 通过Intent的addFlag指定 ,这里指定为 single task
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);*/
}
});
findViewById(R.id.mBtn3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(FirstActivity.this,ThirdActivity.class));
}
});
findViewById(R.id.mBtn4).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(FirstActivity.this,FourtActivity.class));
}
});
}
}
manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.amqr.taskstack" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity android:name=".FirstActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SecondActivity" />
<activity android:name=".ThirdActivity"/>
<activity android:name=".FourtActivity"/>
</application>
</manifest>
.
.
4.2、singletop 单一顶部模式 (顶部不会重复)
结论:如果开启的Activity已经存在一个实例在任务栈的顶部(仅限于顶部),再去开启这个Activity,任务栈不会创建新的Activity的实例了,而是复用已经存在的这个Activity,onNewIntent方法被调用;之前打开过,但不是位于栈顶,那么还是会产生新的实例入栈,不会回调onNewIntent方法。
当我们把一个Activity设置为singleTop,当我们点击打开这个Activity的时候,我们打开B页面,会出现几种情况:
说明:当前A和C都是Standard,B是singleTop
之前没打开过:
此时任务栈里面只有A,A所在的任务栈是S1,这个时候打开singleTop的B,B入栈,入的是S1这个栈,谁打开它进入谁的栈,此时S1的情况是BA,B为栈顶。
之前打开过,但是位于栈顶:
那么复用这个栈,不会有新的实例压入栈中。同时 onNewIntent 方法会被回调,我们可以利用这个方法获得页面传过来的消息或者其他操作。
之前打开过,但是不是位于栈顶:
那么还是会产生新的实例入栈。
情景实测
实测前,我们不改动Standard的代码,只是在manifest里面的将SecondActivity的启动模式设置为singleTop
<activity android:name=".SecondActivity"
android:launchMode="singleTop" />
以下的谈论都是在A和C都是Standard模式,B为singleTop模式的情况进行的。
- 情景一:打开A,然后在打开B
没什么好说的,很普通的没什么区别
请出终端大哥
Running activities (most recent first):
TaskRecord{529c0838 #19 A=com.amqr.taskstack U=0 sz=2}
Run #1: ActivityRecord{52a35f58 u0 com.amqr.taskstack/.SecondActivity t19}
Run #0: ActivityRecord{52a6c77c u0 com.amqr.taskstack/.FirstActivity t19}
生命周期log
12-03 07:57:20.728 6639-6639/? D/Cur: FirstActicity onCreate
12-03 07:57:20.728 6639-6639/? D/Cur: FirstActivity onStart
12-03 07:57:20.728 6639-6639/? D/Cur: FirstActivity onResume
12-03 07:57:21.988 6639-6639/com.amqr.taskstack D/Cur: FirstActivity onPause
12-03 07:57:22.004 6639-6639/com.amqr.taskstack D/Cur: SecondActivity onCreate
12-03 07:57:22.004 6639-6639/com.amqr.taskstack D/Cur: SecondActivity onStart
12-03 07:57:22.004 6639-6639/com.amqr.taskstack D/Cur: SecondActivity onResume
12-03 07:57:22.428 6639-6639/com.amqr.taskstack D/Cur: FirstActivity onStop
- 情况2:打开A,打开B,然后再打开B
这个时候singleTop的作用就发挥出来了。这是不会产生新的实例,会复用的顶部的已经存在的实例。
第一次打开B,这时任务栈里面的顺序是BA,B为栈顶
第二次打开B,这时任务栈里面的顺序还是BA,B为栈顶。
请出终端大哥:
Running activities (most recent first):
TaskRecord{52b29134 #21 A=com.amqr.taskstack U=0 sz=2}
Run #1: ActivityRecord{5295bdf8 u0 com.amqr.taskstack/.SecondActivity t21}
Run #0: ActivityRecord{52a395a8 u0 com.amqr.taskstack/.FirstActivity t21}
生命周期log
简单说可以认为,由于在我们不开新的实例的基础上打开自己,所以就只经历了一个 焦点的失去和获取 的过程。
在SecondActivity中复写onNewIntent方法
@Override
protected void onDestroy() {
super.onDestroy();
Log.d("Cur", "SecondActivity onDestroy");
}
// onNewIntent 方法,当被复用时调用
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d("Cur", "SecondActivity onNewIntent");
}
SecondActivity 的onNewIntent方法被调用了,代表被复用。
(需要注意的是,当我们第二次打开B的时候,因为没有新的实例,所以不存在onCreate和onStart方法的执行,也不存在onStop)
12-03 08:02:05.564 9277-9277/? D/Cur: FirstActicity onCreate
12-03 08:02:05.564 9277-9277/? D/Cur: FirstActivity onStart
12-03 08:02:05.564 9277-9277/? D/Cur: FirstActivity onResume
// 第一次打开B
12-03 08:02:10.000 9277-9277/com.amqr.taskstack D/Cur: FirstActivity onPause
12-03 08:02:10.024 9277-9277/com.amqr.taskstack D/Cur: SecondActivity onCreate
12-03 08:02:10.024 9277-9277/com.amqr.taskstack D/Cur: SecondActivity onStart
12-03 08:02:10.024 9277-9277/com.amqr.taskstack D/Cur: SecondActivity onResume
12-03 08:02:10.436 9277-9277/com.amqr.taskstack D/Cur: FirstActivity onStop
// 第二次打开B 生命周期明显发生变化,只剩下 onRause和onResume
12-03 08:02:11.720 9277-9277/com.amqr.taskstack D/Cur: SecondActivity onPause
12-03 08:02:11.724 9277-9277/com.amqr.taskstack D/Cur: SecondActivity onNewIntent
12-03 08:02:11.724 9277-9277/com.amqr.taskstack D/Cur: SecondActivity onResume
- 情况3 ,打开A,打开B,打开C,再打开B
这时虽然B是singleTop,但是由于打开A,打开B,打开C 的步骤走完任务栈里面的顺序是CBA,C是栈顶,因为B不处于栈顶所以不会复用B,此时还是会产生新的B实例进行入栈。
有请终端先生:
Running activities (most recent first):
TaskRecord{52a15e50 #23 A=com.amqr.taskstack U=0 sz=4}
Run #3: ActivityRecord{529d9310 u0 com.amqr.taskstack/.SecondActivity t23}
Run #2: ActivityRecord{529d1b64 u0 com.amqr.taskstack/.ThirdActivity t23}
Run #1: ActivityRecord{5297e728 u0 com.amqr.taskstack/.SecondActivity t23}
Run #0: ActivityRecord{5297b8fc u0 com.amqr.taskstack/.FirstActivity t23}
从上面的终端信息我们知道,B真的还是产生新的实例,没有复用,只要B不在顶部,特技就发挥不出来。
生命周期也是没有什么特别的
12-03 08:10:24.080 11761-11761/com.amqr.taskstack D/Cur: FirstActicity onCreate
12-03 08:10:24.080 11761-11761/com.amqr.taskstack D/Cur: FirstActivity onStart
12-03 08:10:24.080 11761-11761/com.amqr.taskstack D/Cur: FirstActivity onResume
// 第一次打开B
12-03 08:10:26.888 11761-11761/com.amqr.taskstack D/Cur: FirstActivity onPause
12-03 08:10:26.908 11761-11761/com.amqr.taskstack D/Cur: SecondActivity onCreate
12-03 08:10:26.908 11761-11761/com.amqr.taskstack D/Cur: SecondActivity onStart
12-03 08:10:26.908 11761-11761/com.amqr.taskstack D/Cur: SecondActivity onResume
12-03 08:10:27.340 11761-11761/com.amqr.taskstack D/Cur: FirstActivity onStop
// 打开C
12-03 08:10:27.880 11761-11761/com.amqr.taskstack D/Cur: SecondActivity onPause
12-03 08:10:27.888 11761-11761/com.amqr.taskstack D/Cur: ThirdActivity onCreate
12-03 08:10:27.888 11761-11761/com.amqr.taskstack D/Cur: ThirdActivity onStart
12-03 08:10:27.888 11761-11761/com.amqr.taskstack D/Cur: ThirdActivity onResume
12-03 08:10:28.300 11761-11761/com.amqr.taskstack D/Cur: SecondActivity onStop
// 第二次打开B,
12-03 08:10:29.040 11761-11761/com.amqr.taskstack D/Cur: ThirdActivity onPause
12-03 08:10:29.056 11761-11761/com.amqr.taskstack D/Cur: SecondActivity onCreate
12-03 08:10:29.056 11761-11761/com.amqr.taskstack D/Cur: SecondActivity onStart
12-03 08:10:29.056 11761-11761/com.amqr.taskstack D/Cur: SecondActivity onResume
12-03 08:10:29.436 11761-11761/com.amqr.taskstack D/Cur: ThirdActivity onStop
.
.
4.3、singleTask 单一任务 (整个任务栈只有一个对应自身的实例)
结论:如果开启的甲Activity已经存在一个实例在任务栈S1,再去开启这个Activity,位于栈顶则直接复用,回调onNewIntent方法;位于里面,也是复用,回调onNewIntent方法,复用的同时的是直接把自己上方的全部Activity都干掉。
当我们把一个Activity设置为singleTask模式之后,当我们点击开启这个Activity,会出现3种情况:
说明:打开B,A和C是Standard,B是singleTask
之前没开启过:A开启B的时候,B进入A的任务栈。为了顶部
之前开启过情况1:如果现在任务栈情况是BA,B位于栈顶,此时点击B,那么不会创建新的实例,任务栈还是BA,回调onNewIntent方法。
之前开启过情况2:假如现在任务栈情况是CBA,C为了栈顶,那么这时打开B,因为B是singleTask,这时不会创建新的实例,但是肯定会把B置为栈顶(B在回到栈顶的时候不是跳过去的,而是把自己上面的其他Activity全部干掉,这样就只剩下自己和自己下面的Activity了),那么这时任务栈里面的情况就剩下 BA,回调onNewIntent方法.
.
.
情景实测
好啦,情况说完了,现在应该进行实测了。
原先的代码不用改,只需要把SecondActivity指定为singleTask模式,我们这里采用manifes指定
<activity android:name=".SecondActivity"
android:launchMode="singleTask"
/>
以下讨论的都是A和C都为Standard,B为singleTask
- 情况1,打开A,打开B,在打开B。
最终任务栈的结果是 BA. 其中B是栈顶,而且B只有一个,A是栈底。此时onNewIntent方法会被回调。
这个演示需要有一个图片,明确清楚地知道我们第二次打开B的时候是打不开,没反应的。
通过上图,我们知道第二次点击“开启第2个Activity”是没有办法再次打开一个已经采用singleTask而且已经位于顶部的Activity新的实例。因为他就是那里,不会新增一个新的实例。(可以跟Standard的 情况3 做比较)
终端的数据可以证明一切:
Running activities (most recent first):
TaskRecord{529f16ec #2 A=com.amqr.taskstack U=0 sz=2}
Run #1: ActivityRecord{52a52380 u0 com.amqr.taskstack/.SecondActivity t2}
Run #0: ActivityRecord{529bc564 u0 com.amqr.taskstack/.FirstActivity t2}
查看下面的log,对比生命周期。
这里跟单一顶部一样的效果。
12-03 07:11:22.024 11795-11795/com.amqr.taskstack D/Cur: FirstActicity onCreate
12-03 07:11:22.024 11795-11795/com.amqr.taskstack D/Cur: FirstActivity onStart
12-03 07:11:22.024 11795-11795/com.amqr.taskstack D/Cur: FirstActivity onResume
第一次按下打开B
12-03 07:11:30.980 11795-11795/com.amqr.taskstack D/Cur: FirstActivity onPause
12-03 07:11:30.992 11795-11795/com.amqr.taskstack D/Cur: SecondActivity onCreate
12-03 07:11:30.992 11795-11795/com.amqr.taskstack D/Cur: SecondActivity onStart
12-03 07:11:30.992 11795-11795/com.amqr.taskstack D/Cur: SecondActivity onResume
12-03 07:11:31.384 11795-11795/com.amqr.taskstack D/Cur: FirstActivity onStop
第二次按下打开B
12-03 07:11:48.280 11795-11795/com.amqr.taskstack D/Cur: SecondActivity onPause
12-03 07:11:48.280 11795-11795/com.amqr.taskstack D/Cur: SecondActivity onNewIntent
12-03 07:11:48.280 11795-11795/com.amqr.taskstack D/Cur: SecondActivity onResume
- 情况2 打开A,打开B,打开C,打开D,再次打开B。
任务栈最终的情况 BA
这个情况有意思,当我们第二次打开B的时候,他会把自己上方的全部Activity给干掉,最后只剩下自己和他身下的Activity了。
说到底也正常,栈的结构看数据结构就知道了,其实也可以联想到现实,子弹夹我们想把倒数第二的子弹打出来,自然要把他前面的两个子弹给清掉。
分两步来吧看个究竟吧
当我们打开A,打开B,打开C,打开D,任务栈里面的情况是这样滴
召唤终端大哥
Running activities (most recent first):
TaskRecord{52a36d80 #11 A=com.amqr.taskstack U=0 sz=4}
Run #3: ActivityRecord{529be7bc u0 com.amqr.taskstack/.FourtActivity t11}
Run #2: ActivityRecord{5295bdf8 u0 com.amqr.taskstack/.ThirdActivity t11}
Run #1: ActivityRecord{5294692c u0 com.amqr.taskstack/.SecondActivity t11}
Run #0: ActivityRecord{52a5f5f8 u0 com.amqr.taskstack/.FirstActivity t11}
就在这时,任务栈的顺序是:DCBA ,其中D是栈顶。
这时我们按下打开B,倒数第二的B 发射出一道闪闪金光,把上方的C和D全歼了,这时任务栈的顺序就只剩下BA了,B为栈顶,好残忍的说。
然后再次召唤终端出场
Running activities (most recent first):
TaskRecord{52a15cdc #12 A=com.amqr.taskstack U=0 sz=2}
Run #1: ActivityRecord{5294692c u0 com.amqr.taskstack/.SecondActivity t12}
Run #0: ActivityRecord{52a66cc8 u0 com.amqr.taskstack/.FirstActivity t12}
这里在查看一下生命周期的log,发现D和C付出了血的代价,直接被onDestroy了。
12-03 07:24:16.536 23760-23760/? D/Cur: FirstActicity onCreate
12-03 07:24:16.536 23760-23760/? D/Cur: FirstActivity onStart
12-03 07:24:16.536 23760-23760/? D/Cur: FirstActivity onResume
12-03 07:24:20.072 23760-23760/com.amqr.taskstack D/Cur: FirstActivity onPause
12-03 07:24:20.088 23760-23760/com.amqr.taskstack D/Cur: SecondActivity onCreate
12-03 07:24:20.088 23760-23760/com.amqr.taskstack D/Cur: SecondActivity onStart
12-03 07:24:20.088 23760-23760/com.amqr.taskstack D/Cur: SecondActivity onResume
12-03 07:24:20.484 23760-23760/com.amqr.taskstack D/Cur: FirstActivity onStop
12-03 07:24:20.940 23760-23760/com.amqr.taskstack D/Cur: SecondActivity onPause
12-03 07:24:20.956 23760-23760/com.amqr.taskstack D/Cur: ThirdActivity onCreate
12-03 07:24:20.956 23760-23760/com.amqr.taskstack D/Cur: ThirdActivity onStart
12-03 07:24:20.956 23760-23760/com.amqr.taskstack D/Cur: ThirdActivity onResume
12-03 07:24:21.352 23760-23760/com.amqr.taskstack D/Cur: SecondActivity onStop
12-03 07:24:21.708 23760-23760/com.amqr.taskstack D/Cur: ThirdActivity onPause
12-03 07:24:21.720 23760-23760/com.amqr.taskstack D/Cur: FourtActivity onCreate
12-03 07:24:21.720 23760-23760/com.amqr.taskstack D/Cur: FourtActivity onStart
12-03 07:24:21.720 23760-23760/com.amqr.taskstack D/Cur: FourtActivity onResume
12-03 07:24:22.128 23760-23760/com.amqr.taskstack D/Cur: ThirdActivity onStop
12-03 07:24:24.400 23760-23760/com.amqr.taskstack D/Cur: ThirdActivity onDestroy
12-03 07:24:24.404 23760-23760/com.amqr.taskstack D/Cur: FourtActivity onPause
12-03 07:24:24.408 23760-23760/com.amqr.taskstack D/Cur: SecondActivity onNewIntent
12-03 07:24:24.408 23760-23760/com.amqr.taskstack D/Cur: SecondActivity onStart
12-03 07:24:24.408 23760-23760/com.amqr.taskstack D/Cur: SecondActivity onResume
12-03 07:24:24.784 23760-23760/com.amqr.taskstack D/Cur: FourtActivity onStop
12-03 07:24:24.784 23760-23760/com.amqr.taskstack D/Cur: FourtActivity onDestroy
singleTask的谈论至此完毕。
.
.
4.4 singleInstance 单一实例(单例),任务栈里面自已自己一个人
结论:当启动一个启动模式为singleInstance的Activity时(之前没启动过),这时系统将开辟出另外一个任务栈,用于存放这个Activity,而且这个新的任务栈只能存放自身这唯一一个Activity。singleInstance页面作为前台任务打开自己打开自己,则复用,任务栈顺序无变化;singleInstance页面作为后台任务栈,则切换成为前台任务栈,无新实例产生,复用。
复用就会调用onNewIntent方法。
情景实测
以下的A和B都是Standard,然后C和D我们都通manifest把启动模式设定为 singleInstance
<activity android:name=".ThirdActivity"
android:launchMode="singleInstance"
/>
<activity android:name=".FourtActivity"
android:launchMode="singleInstance"
/>
- 情景1:打开A,打开B,打开为singleInstance的C
打开A,A存在于任务栈S1,此时任务栈里面只有A,
打开B, B入栈,进入S1,此时S1的里面有两个Activity,顺序为BA,B为栈顶。
打开C,因为C是singleInstance,所以自己的任务栈。很明显,这个时候C不会进入S1的。而是会开辟出来一个全新的任务栈,我们就称为任务栈S2吧,
注意,此时S2属于前台任务栈,S1属于后台任务栈。
如果非要排个需要序号的话,那么就是 (S2-C)、(S1-B)、(S1-A),前面的是老大。
请出终端大哥:
Running activities (most recent first):
TaskRecord{52a54d24 #31 A=com.amqr.taskstack U=0 sz=1}
Run #2: ActivityRecord{5297e728 u0 com.amqr.taskstack/.ThirdActivity t31}
TaskRecord{52a193f4 #30 A=com.amqr.taskstack U=0 sz=2}
Run #1: ActivityRecord{5297b8fc u0 com.amqr.taskstack/.SecondActivity t30}
Run #0: ActivityRecord{52a5f120 u0 com.amqr.taskstack/.FirstActivity t30}
别只看A="aaa.bbb.ccc"相同, 注意,#num ,比如#31和#30就不同就代表是两个不同任务栈。#号带的数字是最能区分的标志。我们说的 # 是紧挨着任务栈名称左边的那个#。
生命周期没什么影响
12-03 08:32:27.108 24263-24263/com.amqr.taskstack D/Cur: FirstActicity onCreate
12-03 08:32:27.108 24263-24263/com.amqr.taskstack D/Cur: FirstActivity onStart
12-03 08:32:27.108 24263-24263/com.amqr.taskstack D/Cur: FirstActivity onResume
// 打开B
12-03 08:32:28.828 24263-24263/com.amqr.taskstack D/Cur: FirstActivity onPause
12-03 08:32:28.844 24263-24263/com.amqr.taskstack D/Cur: SecondActivity onCreate
12-03 08:32:28.844 24263-24263/com.amqr.taskstack D/Cur: SecondActivity onStart
12-03 08:32:28.844 24263-24263/com.amqr.taskstack D/Cur: SecondActivity onResume
12-03 08:32:29.236 24263-24263/com.amqr.taskstack D/Cur: FirstActivity onStop
// 打开为 singleInstance 的C
12-03 08:32:29.888 24263-24263/com.amqr.taskstack D/Cur: SecondActivity onPause
12-03 08:32:29.892 24263-24263/com.amqr.taskstack D/Cur: ThirdActivity onCreate
12-03 08:32:29.892 24263-24263/com.amqr.taskstack D/Cur: ThirdActivity onStart
12-03 08:32:29.892 24263-24263/com.amqr.taskstack D/Cur: ThirdActivity onResume
12-03 08:32:30.656 24263-24263/com.amqr.taskstack D/Cur: SecondActivity onStop
- 情景2、打开A,打开B,打开为singleInstance的C,然后按下返回键,第二次按返回键,第三次再按下返回键。
也就是在情景1的条件下再按下返回键。
我们再续前缘接着说。
第一次按下返回键,S2销毁,S1成为前台任务栈,S1任务栈里面的顺序是BA,其中B是栈顶。
界面停留在B界面。
第二次按下返回键,界面退到A界面,S1任务里面B出栈,任务栈里面只剩A。
第三次按下返回键,整个程序退出,任务栈S1销毁。
- 情景3,打开A,打开B,打开singleInstance的C,C打开C(复用,无新实例),接着打开singleInstance的D,接着D打开C。(复用,无新实例,改变任务栈顺序)
C和D都复写onNewIntent方法以便观察。
那么这时会产生3个任务栈,A和B所在的为S1,C所在的任务栈为S2,D所所在的任务栈为S3.
步骤一:
打开A,打开B,B打开的C
终端
Running activities (most recent first):
TaskRecord{529819f4 #44 A=com.amqr.taskstack U=0 sz=1}
Run #2: ActivityRecord{529cfc00 u0 com.amqr.taskstack/.ThirdActivity t44}
TaskRecord{52a05480 #43 A=com.amqr.taskstack U=0 sz=2}
Run #1: ActivityRecord{529c2564 u0 com.amqr.taskstack/.SecondActivity t43}
Run #0: ActivityRecord{529a6e24 u0 com.amqr.taskstack/.FirstActivity t43}
生命周期
12-05 23:10:11.178 15891-15891/? D/Cur: FirstActicity onCreate
12-05 23:10:11.178 15891-15891/? D/Cur: FirstActivity onStart
12-05 23:10:11.178 15891-15891/? D/Cur: FirstActivity onResume
// 打开B
12-05 23:10:16.166 15891-15891/com.amqr.taskstack D/Cur: FirstActivity onPause
12-05 23:10:16.174 15891-15891/com.amqr.taskstack D/Cur: SecondActivity onCreate
12-05 23:10:16.174 15891-15891/com.amqr.taskstack D/Cur: SecondActivity onStart
12-05 23:10:16.174 15891-15891/com.amqr.taskstack D/Cur: SecondActivity onResume
12-05 23:10:16.558 15891-15891/com.amqr.taskstack D/Cur: FirstActivity onStop
// 第一次打开C
12-05 23:10:17.826 15891-15891/com.amqr.taskstack D/Cur: SecondActivity onPause
12-05 23:10:17.826 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onCreate
12-05 23:10:17.826 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onStart
12-05 23:10:17.826 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onResume
12-05 23:10:18.622 15891-15891/com.amqr.taskstack D/Cur: SecondActivity onStop
.
.
步骤2:再次打开C,复用,终端信息一致。就不附上了。
生命周期有所不同。
12-05 23:10:11.178 15891-15891/? D/Cur: FirstActicity onCreate
12-05 23:10:11.178 15891-15891/? D/Cur: FirstActivity onStart
12-05 23:10:11.178 15891-15891/? D/Cur: FirstActivity onResume
// 打开B
12-05 23:10:16.166 15891-15891/com.amqr.taskstack D/Cur: FirstActivity onPause
12-05 23:10:16.174 15891-15891/com.amqr.taskstack D/Cur: SecondActivity onCreate
12-05 23:10:16.174 15891-15891/com.amqr.taskstack D/Cur: SecondActivity onStart
12-05 23:10:16.174 15891-15891/com.amqr.taskstack D/Cur: SecondActivity onResume
12-05 23:10:16.558 15891-15891/com.amqr.taskstack D/Cur: FirstActivity onStop
// 第一次打开C
12-05 23:10:17.826 15891-15891/com.amqr.taskstack D/Cur: SecondActivity onPause
12-05 23:10:17.826 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onCreate
12-05 23:10:17.826 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onStart
12-05 23:10:17.826 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onResume
12-05 23:10:18.622 15891-15891/com.amqr.taskstack D/Cur: SecondActivity onStop
// 第二次打开C
12-05 23:10:20.650 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onPause
12-05 23:10:20.650 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onNewIntent
12-05 23:10:20.650 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onResume
步骤3:C打开D
Running activities (most recent first):
TaskRecord{529f9160 #45 A=com.amqr.taskstack U=0 sz=1}
Run #3: ActivityRecord{529f8ff8 u0 com.amqr.taskstack/.FourtActivity t45}
TaskRecord{529819f4 #44 A=com.amqr.taskstack U=0 sz=1}
Run #2: ActivityRecord{529cfc00 u0 com.amqr.taskstack/.ThirdActivity t44}
TaskRecord{52a05480 #43 A=com.amqr.taskstack U=0 sz=2}
Run #1: ActivityRecord{529c2564 u0 com.amqr.taskstack/.SecondActivity t43}
Run #0: ActivityRecord{529a6e24 u0 com.amqr.taskstack/.FirstActivity t43}
生命周期
接着上次的LOG
// 第二次打开C
12-05 23:10:20.650 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onPause
12-05 23:10:20.650 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onNewIntent
12-05 23:10:20.650 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onResume
// C打开D
12-05 23:32:29.242 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onPause
12-05 23:32:29.246 15891-15891/com.amqr.taskstack D/Cur: FourtActivity onCreate
12-05 23:32:29.246 15891-15891/com.amqr.taskstack D/Cur: FourtActivity onStart
12-05 23:32:29.246 15891-15891/com.amqr.taskstack D/Cur: FourtActivity onResume
12-05 23:32:30.038 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onStop
发现多了一个任务栈。
步骤4 D打开C
终端
Running activities (most recent first):
TaskRecord{529819f4 #44 A=com.amqr.taskstack U=0 sz=1}
Run #3: ActivityRecord{529cfc00 u0 com.amqr.taskstack/.ThirdActivity t44}
TaskRecord{529f9160 #45 A=com.amqr.taskstack U=0 sz=1}
Run #2: ActivityRecord{529f8ff8 u0 com.amqr.taskstack/.FourtActivity t45}
TaskRecord{52a05480 #43 A=com.amqr.taskstack U=0 sz=2}
Run #1: ActivityRecord{529c2564 u0 com.amqr.taskstack/.SecondActivity t43}
Run #0: ActivityRecord{529a6e24 u0 com.amqr.taskstack/.FirstActivity t43}
生命周期
接上次的C打开D的Log
// C打开D
12-05 23:32:29.242 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onPause
12-05 23:32:29.246 15891-15891/com.amqr.taskstack D/Cur: FourtActivity onCreate
12-05 23:32:29.246 15891-15891/com.amqr.taskstack D/Cur: FourtActivity onStart
12-05 23:32:29.246 15891-15891/com.amqr.taskstack D/Cur: FourtActivity onResume
12-05 23:32:30.038 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onStop
// D打开C
12-05 23:56:10.402 15891-15891/com.amqr.taskstack D/Cur: FourtActivity onPause
12-05 23:56:10.402 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onNewIntent
12-05 23:56:10.402 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onStart
12-05 23:56:10.402 15891-15891/com.amqr.taskstack D/Cur: ThirdActivity onResume
12-05 23:56:11.154 15891-15891/com.amqr.taskstack D/Cur: FourtActivity onStop
没有新的任务栈产生,前台任务栈发了变化。任务栈里面也不会有新的Activity产生。
singleInstance分析至此结束。
5、另说singleTask的小伙伴:taskAffinity + allowTaskReparenting
说在前面
taskAffinity是singleTask的好伙伴,这是肯定的。
按照官网大概是这么说taskAffinity 和 allowTaskReparenting的:
taskAffinity: 可以指定一个Activity放入哪一个任务栈中,利用taskAffinity制定一个任务栈的名称,把Activity放进这个任务栈中。实现这个过程需要singleTask和allowTaskReparenting两者的协助。
allowTaskReparenting:参数是boolean,如果我们利用taskAffinity让Activity放入一个指定的任务栈,需要allowTaskReparenting的同意,为true就可以跟着别人跑,为false就乖乖在原地呆着那都不许去。
按照的官网的说法,实测后发现,taskAffinity确实可以让Activity跑到指定的任务栈(不用跟app自带的任务栈混了),但是allowTaskReparenting没什么作用,设置或者不设置没什么改变。设置为true,设置为false,或者不设置,一点都没区别。
即是说官方说,taskAffinity要和allowTaskReparenting配合着使用,实测是上不用,taskAffinity单兵作战也是可以的。(个人看法,疏忽之处请熟悉的朋友指点一下)
下面开始正式分析。
5.1、taskAffinity
taskAffinity属性用于给Activity单独指定任务栈名称。这个名称不能和包名相同,否则就没有意义。注意taskAffinity属性值为String,而且中间必须包含有分隔符 . (英文状态下的点),比如com.baidu.test
另外,如果想要指定一个非包名的任务栈,该Activity一定要把启动模式设置为singleTask模式,否则不会生效。如果taskAffinity指定的名称是其他程序的包名,那么可以不结合singleTask。念起来好像有点拗口,看下面的实测就知道怎么回事了。
注意:任务栈分为前台任务栈和后台任务栈,后台任务栈里面的Activity全部处于onStop状态。
在minifest里面,application可以设定taskAffinity,activity也可以设定taskAffinity。
taskAffinity设定的任务栈我们也称其为一个宿主任务栈。
-
application设定
- applicatipn如果不设定,那么就系统默认设定为包名。如果设定了,activity跟着application走,application指定的是什么activity的任务栈的名称就是什么。(application自带的不设定,一般我们也不手动设定,要设定也是单独在activity里面设定)
-
activity设定
- 设定taskAffinity之后,当启动这个Activity之后,如果之前没有任务栈的存在,那么就启动系统会开辟出来一个新的任务栈(或者叫宿主任务栈),用于存放这个activity,如果已经存在了这个任务栈,那么这个activity就对应进入已经的宿主任务栈。(设定taskAffinity,不管之前存不存在,反正就不跟默认的混了,自己只认指定的任务栈,当然,如果你非要把taskAffinity指定自己的包名那也没办法,只是那没撒意思嘛)
我们利用taskAffinity+taskAffinity指定非手机里面任何程序的包名的任务栈时,这个任务是可以容纳多个activity的。比如现在有A,B,C三个界面,B和C都启动模式都是taskAffinity,而且taskAffinity指定的都是同一个包名。
那么当我们A开启B,B再开启C的时候,结果就是A在app默认自带的任务栈S1里,而B和C在同一个任务栈S2里面。这是S2里面的顺序是CB,C是栈顶。然后这个时候从C打开B,那么事情来了,本来的C下面的B会再次汇聚天地能量,发出一道闪闪金光,把自己上方的所有Activity全部歼灭,这个时候C就挂了。可见,不管和谁合作,singleTask依旧霸道,不可阻挡,谁挡谁卒。
有代码有真相,先来看一下如何利用taskAffinity给activty指定任务栈:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.amqr.newtaskone" >
<activity android:name=".OtherActivity"
// android:taskAffinity 的使用一般都是有singeTask一起出现的
android:taskAffinity="com.task.try"
/>
如上,我们的包名是 com.amqr.newtaskone,然后我们给我们的activity指定了宿主任务栈的的名称为 com.task.try 。这样他就和系统默认的任务栈名称不同了。
5.2、allowTaskReparenting
allowTaskReparenting:参数是boolean。
如果我们利用taskAffinity让Activity放入一个指定的任务栈,需要allowTaskReparenting的同意,为true就可以跟着别人跑,为false就乖乖在原地呆着除了自己家那都不许去。
怎么用
<activity android:name=".OtherActivity"
android:taskAffinity="com.task.try"
android:allowTaskReparenting="true"
/>
5.3 实测
情况1和情况2,app2的activity指定的任务栈名称是app1的包名,所以不需要singleTask(这里指定包名是为了演示效果)
- 情况1:新建两个app。
app1什么都不改,自带一个MainActivity(布局文件标识一下是app1的Mainactivity)。
在app2的manifest给app2的MainAcivity添加android:taskAffinity属性,指定包名为 app1的包名。
也就是说,当app2的MainActivity的启动的时候会把app的默认任务栈当做自己的宿主。
我们把两个程序安装一下,清掉其他正在运行的程序
运行app1,然后按下home键(让app1变成后台程序),接着打开app2,我们会发现,这时候进入的不是app2的界面,而是app1的。
先来看一下两个app的Mainactivty本来的面目:
app1的MainActivityUI截图
app2的MainActivityUI截图
接下来看一下各自的manifest
app1的manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.amqr.taskaffinity1" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
>
<activity android:name=".MainActivity1">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
app2的manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.amqr.taskaffinity2" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity android:name=".MainActivity2"
android:taskAffinity="com.amqr.taskaffinity1"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
查看效果:
终端大哥也显示为只有一个任务栈,那就是app1自带的那个com.amqr.taskaffinity1
Running activities (most recent first):
TaskRecord{5296a83c #4 A=com.amqr.taskaffinity1 U=0 sz=1}
Run #0: ActivityRecord{52983d74 u0 com.amqr.taskaffinity1/.MainActivity1 t4}
生命周期
12-03 22:50:46.324 1835-1835/com.amqr.taskaffinity1 D/Cur: MainActivity1, onCreate
12-03 22:50:46.324 1835-1835/com.amqr.taskaffinity1 D/Cur: MainActivity1, onStart
12-03 22:50:46.324 1835-1835/com.amqr.taskaffinity1 D/Cur: MainActivity1, onResume
// 按下home键
12-03 22:50:48.356 1835-1835/com.amqr.taskaffinity1 D/Cur: MainActivity1, onPause
12-03 22:50:48.872 1835-1835/com.amqr.taskaffinity1 D/Cur: MainActivity1, onStop
// 启动app2,结果打开是app1
12-03 22:50:52.868 1835-1835/com.amqr.taskaffinity1 D/Cur: MainActivity1, onRestart
12-03 22:50:52.868 1835-1835/com.amqr.taskaffinity1 D/Cur: MainActivity1, onStart
12-03 22:50:52.868 1835-1835/com.amqr.taskaffinity1 D/Cur: MainActivity1, onResume
我们发现,我们点击启动的app2,结果启动的却是app1,而且,在运行列表里面看不到app2的存在,只有一个app1。
里面发生的是这样的一个过程,
当我们启动app1时:app1的任务栈com.amqr.taskaffinity1 正常作为一个任务栈进入系统,
按下home键时:任务栈 com.amqr.taskaffinity1 成为了一个后台任务栈
点击app2时: 本来应该打开的是app2的界面,但是要打开的MainActivity2发现自身含有android:taskAffinity,而指定的宿主任务栈就是app1打开后就存在的任务栈 com.amqr.taskaffinity1 ,那么这时候 com.amqr.taskaffinity1 就成为了 MainActivity2 的宿主任务栈。
按道理,这个时候本来应该是 MainActivity2 入栈而且作为栈顶的啊,但是事实却没有这么发生。也就是说,这个时候,MainActivity2没有入栈,对的,没有入栈。他是跑过来搞笑的,通知了一下别人可以变成前台任务栈了,然后自己没有入栈,具体详情,不得而知。
实际上发生的事情是:com.amqr.taskaffinity1 由后台任务栈转为前台任务栈,而那个MainActity没有入栈,从终端的信息就可以看得出来。
有人说,你没按谷歌的来,没有把allowTaskReparenting设置为true,好,那我们进行其概况2,接着来一遍。
- 情况2 还是上面的代码,我们只是app2的manifest里面给MainActivity加上一句
android:allowTaskReparenting="true"
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.amqr.taskaffinity2" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity android:name=".MainActivity2"
android:taskAffinity="com.amqr.taskaffinity1"
android:allowTaskReparenting="true"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
我们重新安装两个程序,接着来一遍。
(清除所有运行的程序,打开app1,按下home,打开app2)
结果发现一样,还是贴一下对应的终端信息吧
Running activities (most recent first):
TaskRecord{52992bc4 #10 A=com.amqr.taskaffinity1 U=0 sz=1}
Run #0: ActivityRecord{52a97bdc u0 com.amqr.taskaffinity1/.MainActivity1 t10}
一个样,其实就算设置为false也是没有变化,有兴趣的可以自己试一下。
注意:宿主不是绝对化的,当两个app都认定一个宿主后,�就先来后到了,谁先开启那个任务栈谁是老大。比如说,我们在可以清掉所有运行的程序,先开启app2,然后再开启app1,我们会发现打开的是app2,就不是app1了。
- 情况三:
情况三不是指定程序的包名作为指定的任务栈名称,所以需要启动的模式singleTask一起工作。(如果不跟singleTask合作,又不指定为程序的包名,那么设置android:taskAffinity不生效)
情况三我们这样弄,app1和app2各自都有两个页面,各自的启动页都是普通常见的,然后各自的都有另外一个其他页面,这分属于两个app的其他页面我们都给他们指定同一个任务栈名(这个任务站名不跟任意一个app包名相同)
也就是说,app1里面有MainActivity1和App1Other两个Activity,
app2里面有MainActivity2和App2Other两个Activity
MainActivity1和MainActivity2都是普通的Standard
而App1Other和App2Other都在清单文件指定了自定义的任务栈名称和singelTask启动模式。详见下方代码
我们这样操作:
第一阶段:先打开app1,再打开App1Other页面,接着按下home键
第二阶段:(接按下home件之后)打开app2,打开app2的App2Other页面
先贴上相关图片和代码先:
app1的manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.amqr.taskaffinity1" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
>
<activity android:name=".MainActivity1">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".App1Other"
android:taskAffinity="com.amqr.independent"
android:launchMode="singleTask"
/>
</application>
</manifest>
.
.
app2的manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.amqr.taskaffinity2" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity android:name=".MainActivity2"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".App2Other"
android:taskAffinity="com.amqr.independent"
android:launchMode="singleTask"
/>
</application>
</manifest>
- 第一阶段,
我们先打开app1,然后打开的app1Other界面。按下home键
效果如下图:打开第一个activity,然后再点击打开app1Other,按下home键盘,查看运行列表,会看见变成了两个任务栈(任务栈是两个,程序还是一个,进程更加只是一个)
任务栈信息
Running activities (most recent first):
TaskRecord{52a8eab0 #48 A=com.amqr.independent U=0 sz=1}
Run #1: ActivityRecord{52ae1518 u0 com.amqr.taskaffinity1/.App1Other t48}
TaskRecord{52a9cd20 #47 A=com.amqr.taskaffinity1 U=0 sz=1}
Run #0: ActivityRecord{52a9f230 u0 com.amqr.taskaffinity1/.MainActivity1 t47}
Recent tasks:
* Recent #0: TaskRecord{52973240 #52 A=com.amqr.independent U=0 sz=1}
* Recent #1: TaskRecord{52979df8 #51 A=com.amqr.taskaffinity1 U=0 sz=1}
* Recent #2: TaskRecord{5295e6f8 #1 A=com.android.launcher U=0 sz=1}
* Recent #3: TaskRecord{52a90f20 #46 A=com.android.systemui U=0 sz=0}
生命周期
12-04 01:53:51.808 18259-18259/? D/Cur: MainActivity1, onCreate
12-04 01:53:51.808 18259-18259/? D/Cur: MainActivity1, onStart
12-04 01:53:51.808 18259-18259/? D/Cur: MainActivity1, onResume
12-04 01:53:55.704 18259-18259/com.amqr.taskaffinity1 D/Cur: MainActivity1, onPause
12-04 01:53:55.708 18259-18259/com.amqr.taskaffinity1 D/Cur: App1Other, onCreate
12-04 01:53:55.708 18259-18259/com.amqr.taskaffinity1 D/Cur: App1Other, onStart
12-04 01:53:55.708 18259-18259/com.amqr.taskaffinity1 D/Cur: App1Other, onResume
12-04 01:53:56.468 18259-18259/com.amqr.taskaffinity1 D/Cur: MainActivity1, onStop
从上面的图文我们知道,
当我们点击app1时,默认的用的是系统的根据包名定的默认任务栈,这个没什么可说的。
但是当我们点击打开 app1Other 页面时,就多出来一个任务栈,栈名是我们指定的com.amqr.independent。
而且观察运行程序的列表,发现多了个图标,但是要知道,这个绝对不是多了一个进程,只是我们每多产生多一个任务栈,任务列表就会多出来一个图标。
进程不变
- 第二阶段
紧接上面的环节
接下来我们来打开app2,然后打开app2的 app2Other 界面
(完整的操作:打开app1,打开app1Other界面,按下home,打开app2,打开app2的Other界面)
终端信息
Running activities (most recent first):
TaskRecord{52a2e064 #62 A=com.amqr.independent U=0 sz=2}
Run #3: ActivityRecord{529d1550 u0 com.amqr.taskaffinity2/.App2Other t62}
TaskRecord{52a337a0 #63 A=com.amqr.taskaffinity2 U=0 sz=1}
Run #2: ActivityRecord{5299485c u0 com.amqr.taskaffinity2/.MainActivity2 t63}
TaskRecord{52a2e064 #62 A=com.amqr.independent U=0 sz=2}
Run #1: ActivityRecord{529827a0 u0 com.amqr.taskaffinity1/.App1Other t62}
TaskRecord{528a1e60 #61 A=com.amqr.taskaffinity1 U=0 sz=1}
Run #0: ActivityRecord{5296bc68 u0 com.amqr.taskaffinity1/.MainActivity1 t61}
程序截图
我们发现了,
图片的任务列表是3个,终端的日志任务栈看起来像出现了4个,其实是3个任务栈,因为有两个是重复的,为什么重复了呢?
我们先来看一下这里面的两句:
TaskRecord{52a2e064 #62 A=com.amqr.independent U=0 sz=2}
TaskRecord{52a2e064 #62 A=com.amqr.independent U=0 sz=2}
我们通过名字com.amqr.independent可以知道这肯定是同一个任务栈
其实这个sz后面表示的就是activity的个数(已测)
至于为什么分开显示,这个应该是终端显示的一种表示方法,终端的具体规则不太清楚。这里不是我们深究的方向。到了这里明白运行的是3个任务栈就好了。
我们这里了明白singleTask + taskAffinity是如何使用的就好了。
- 情况4
app1里面弄3个activity,MainActivity,App1Other,App3Activity,分别用A,B,C指代,A是Standard的启动页,B和C都是single + taskAffinity且指定的任务栈名称相同(不跟任何程序包名相同)。
操作过程,
第一阶段:运行app1,启动页A启动B,
第二阶段:
再通过B启动C
最后我们发现,在singleTask + taskAffinity模式下还是谁当灭灭谁,在它前面的全部遭殃。
贴上代码和相关图片:
manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.amqr.taskaffinity1" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
>
<activity android:name=".MainActivity1">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".App1Other"
android:taskAffinity="com.amqr.independent"
android:launchMode="singleTask"
/>
<activity android:name=".App3Activity"
android:taskAffinity="com.amqr.independent"
android:launchMode="singleTask"
/>
</application>
</manifest>
第一阶段:
A开启B,B开启C,这时候我们的指定任务栈里面的顺序就是CB
第一阶段终端信息
Running activities (most recent first):
TaskRecord{52983174 #116 A=com.amqr.independent U=0 sz=2}
Run #2: ActivityRecord{5290923c u0 com.amqr.taskaffinity1/.App3Activity t116}
Run #1: ActivityRecord{52a80e58 u0 com.amqr.taskaffinity1/.App1Other t116}
TaskRecord{5299bf74 #115 A=com.amqr.taskaffinity1 U=0 sz=1}
Run #0: ActivityRecord{52a32b30 u0 com.amqr.taskaffinity1/.MainActivity1 t115}
第一阶段结束。
.
.
第二阶段:
紧接着上面的操作,在上面的操作里面我们的指定的任务栈里面已经有了两个都是singleTask的任务在里面了,B在C的下面,C是栈顶,这个时候我们从C开启B,结果C卒,挂了。
第二阶段终端信息:
Running activities (most recent first):
TaskRecord{5295e6f8 #1 A=com.android.launcher U=0 sz=1}
Run #1: ActivityRecord{5295cb64 u0 com.android.launcher/com.android.launcher2.Launcher t1}
TaskRecord{52abf0cc #117 A=com.android.systemui U=0 sz=1}
Run #0: ActivityRecord{5290923c u0 com.android.systemui/.recent.RecentsActivity t117}
本篇就先说道这里吧,下次说标志位—— FLAG.
对5.2情况3有兴趣的可以参考这里Android-Activity5.3情景3之完整Log参考
完。