先看下面的问题
- Activity打开A->B(singleInstance)->C,此时一共有多少个栈保存Actvity?在C页面点击返回键,出站顺序是?
- Activity打开A(singleTask)->B-C,在C页面打开A,此时栈内还剩下多少个Actvity?
栈内信息查看
先通过以下三步查看actvity栈信息
step1:adb链接测试设备
setp2:adb shell
setp3:dumpsys activity activities | sed -En -e '/Stack #/p' -e '/Running activities/,/Run #0/p'
完成后可以看到打印的luncher栈信息:
栈内信息.png
打印各个字符代表的意义?
TaskRecord.java
@Override
public String toString() {
StringBuilder sb = new StringBuilder(128);
if (stringName != null) {
sb.append(stringName);
sb.append(" U=");
sb.append(userId);
sb.append(" StackId=");
sb.append(getStackId());
sb.append(" sz=");
sb.append(mActivities.size());
sb.append('}');
return sb.toString();
}
sb.append("TaskRecord{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" #");
sb.append(taskId);
if (affinity != null) {
sb.append(" A=");
sb.append(affinity);
} else if (intent != null) {
sb.append(" I=");
sb.append(intent.getComponent().flattenToShortString());
} else if (affinityIntent != null) {
sb.append(" aI=");
sb.append(affinityIntent.getComponent().flattenToShortString());
} else {
sb.append(" ??");
}
stringName = sb.toString();
return toString();
}
...
int getStackId() {
return mStack != null ? mStack.mStackId : INVALID_STACK_ID;
}
从toString方法可以看出1-7的意义:
- 代表当前栈对象内存地址
- 当前任务栈ID(这个看出一共有多少个栈)
- taskAffinity属性,如果没有配置,默认为app包名
- userId
- 当前栈id,应该和2的值是一致的,但是和2取值方式不同,通过取值ActivitySatck的mStackId,查看方式ActivitySatck.java源码mStackId,mStackId由final修饰,而且未赋值,所以默认值为0
- 栈内activity数量
- activity名称,排列信息为栈顶到栈底
standard模式
标准启动模式,也是activity的默认启动模式。在这种模式下启动的activity可以被多次实例化,即在同一个任务中可以存在多个activity的实例,每个实例都会处理一个Intent对象。
启动顺序:A->B->C
栈信息:
standard.png
singleInstance模式
总是在新的任务中开启,并且这个新的任务中有且只有这一个实例,也就是说被该实例启动的其他activity会自动运行于另一个任务中。当再次启动该activity的实例时,会重用已存在的任务和实例。并且会调用这个实例的onNewIntent()方法,将Intent实例传递到该实例中。和singleTask相同,同一时刻在系统中只会存在一个这样的Activity实例。
启动顺序:A->B(singleInstance)->C
栈信息:
QQ图片20210317164021.png
此时点击返回键会怎么样?
ezgif-3-b6c60de7f05a.gif
返回顺序:C->A->B
这里就可以回答第一个问题了
答案(1):一共有两个栈,出栈顺序为C->A->B
原因:通过栈信息的ID可以看出CA在同一个栈里面(#71),B在一个栈里面(#72)。C在栈顶出完后在点返回键,所以会是A页面,(#71)栈清空后,就显示(#72)栈顶Activity
如果启动顺序改为:A->B(singleInstance)->C->B(singleInstance)?
答案:还是两个栈,不过第一个栈为(#72)这里和singleTop类似
singleTask模式
谷歌的官方文档上称,如果一个activity的启动模式为singleTask,那么系统总会在一个新任务的最底部(root)启动这个activity,并且被这个activity启动的其他activity会和该activity同时存在于这个新任务中。如果系统中已经存在这样的一个activity则会重用这个实例,并且调用他的onNewIntent()方法。即,这样的一个activity在系统中只会存在一个实例。
启动顺序:A->B(singleTask)->C
指定taskAffinity(com.test).png
启动顺序:A(singleTask)->B->C->A(singleTask)-返回键
C跳转到A
可以看出当C跳转到A时,栈里面只剩下A页面了,通过打印日志B调用了(destroy),C调用了(onStop->onDestroy)
在点击返回键,app会回到桌面了
ezgif-3-b6c60de7f05a.gif
所以启动模式为singleTask的activity,再次打开时,它会把它同栈上面的activity全部弹出销毁,它需要在栈顶
singleTop模式
如果一个以singleTop模式启动的activity的实例已经存在于任务桟的桟顶,那么再启动这个Activity时,不会创建新的实例,而是重用位于栈顶的那个实例,并且会调用该实例的onNewIntent()方法将Intent对象传递到这个实例中。举例来说,如果A的启动模式为singleTop,并且A的一个实例已经存在于栈顶中,那么再调用startActivity(new Intent(this,A.class))启动A时,不会再次创建A的实例,而是重用原来的实例,并且调用原来实例的onNewIntent()方法。这是任务桟中还是这有一个A的实例。
如果以singleTop模式启动的activity的一个实例已经存在与任务桟中,但是不在桟顶,那么它的行为和standard模式相同,也会创建多个实例。
启动顺序:A->B(singleTop)->C->B(singleTop)->C
总结一句:栈顶复用模式,没在栈顶就创建一个,在栈顶就调用onNewIntent方法
Task结构
未命名文件.jpg总结
- ProcessRecord变量activities保存ActivityRecord
- activities中各个ActivityRecord属于不同的TaskRecord
- 打开同taskAffinity的activity,会归类到对应TaskRecord(singleInstance模式打开的除外)
- 新打开的activity,会将对应的TaskRecord排序栈顶
- singleInstance模式打开的,会新建一个TaskRecord,如果未指定taskAffinity,会使用包名为taskAffinity
- actvity出栈根据TaskRecord排序,先清空TaskRecord,从TaskRecord栈顶依次出栈
网友评论