Android管理任务栈之—launchMode
文章目录
前言
之前遇到了一个问题,大概是有 4 个 Activity,分别为 A, B, C, D Activity,当你的 Activity 跳转, A -> B -> C -> D 跳转,此时,你需要从 D 跳回 B,不能把 Cfinish()
掉,因为用户中途可能会返回 C,而不继续进行下一步操作;直接跳也不可以,因为默认情况下跳到 B 的时候,用户点返回会返回 D 而不是 A,这个时候我知道了 LaunchMode,发现这种情况很适合一个 LaunchMode 的使用 ———— singleTask
,趁此机会,研究一下 LaunchMode。
LaunchMode 开篇
说到 LaunchMode,我们来看看官方文档怎么说 :
An instruction on how the activity should be launched. There are four modes that work in conjunction with activity flags (
FLAG_ACTIVITY_*
constants) in Intent objects to determine what should happen when the activity is called upon to handle an intent. They are:
“standard”
“singleTop”
“singleTask”
“singleInstance”
The default mode is “standard”.
即 Activity 有 4 种启动模式,分别是
- “standard”
- “singleTop”
- “singleTask”
- “singleInstance”
这个需要在 Manifest 中指定。可以和 Intent 中的FLAG_ACTIVITY_*
结合使用。
当然,我们要明确一点,Android 中以栈的形式来管理Activity
,至于什么是栈,请复习数据结构相关内容,这里假设大家都理解栈。
四种模式各个模式的作用
先告诉大家一个方法,通过如下命令获取你当前包的Activity
任务栈
adb shell dumpsys activity activities | sed -En -e '/Stack #/p' -e '/Running activities/,/Run #0/p' | grep "包名"
- “standard”
大家默认新建的Activity
就是这个模式的,这个模式相当于不对Activity
加任何约束,即你调用intent.startActivity()
的时候,每调用一次,就会在栈中新加一个改Activity
。
先看一下我们当前的任务栈:
Run #7: ActivityRecord{ac97db9 u0 com.air.activitylaunchmodetestdemo/.MainActivity t1211}
可以看到,当前只有一个MainActivity
,此时,当我点击按钮执行startActivity(new Intent(this, MainActivity.class))
的时候,可以看到任务栈变成了:
Run #8: ActivityRecord{11e0192c u0 com.air.activitylaunchmodetestdemo/.MainActivity t1211}
Run #7: ActivityRecord{ac97db9 u0 com.air.activitylaunchmodetestdemo/.MainActivity t1211}
因为没有在startActivity()
之后执行finish()
方法,现在我们有了两个MainActivity
实例,此时如果退出应用,要点两次后退键才可以。如果我再次点击按钮执行startActivity(new Intent(this, MainActivity.class))
的时候,可以想象新的任务栈该变成什么了。
接下来我们以图的形式说明该 LaunchMode 情况下,任务栈的样子。
就像上图画的那样,每启动一个新的 Activity,就会添加到栈上,无论即将启动的 Activity 是否是同一个 Activity。
- “singleTop”
看到这个名字,先靠命名理解一下,在顶部的时候是单一存在,因为是以栈的形式来管理Activity
的,那么顶部指的就是栈的顶部。
好了,上面的猜测其实已经和真正的情况是差不多了,我在此再补充几点细节。对于不是栈顶的Activity
,即使设置了这个 launchMode,依旧会创建新的 Activity。例如,存在两个Activity
A、B,它们的 launchMode 都是 singleTop,如果你在 A 中启动 A,栈不会新添加Activity
,因为此时 AActivity
在栈顶,但是,如果你用 A 启动 B,B 就会被加入到栈中,此时栈就变成了 AB,再拿 B 启动 A,会产生 A 的新实例添加到栈内,现在栈变成了 ABA。
拿前面举的 AActivity
例子来说,在 A 中启动 A,由于 A 在栈顶,所以栈内不会添加 A 的新实例,自然不会调用 A 的onCreate()
方法,这个时候,如果启动 A 的时候给 A 传了额外的参数,那么需要在哪里处理呢?答案是onNewIntent()
,这里就是 singleTop 和 standard 两种 launchMode 不同的地方。
常用场景:带搜索功能的Activity
,搜索不同的关键词不用多次启动同一个Activity
。
- “singleTask”
上面介绍的两个 launchMode 的作用不能实现栈中只存在一个Activity
实例,但是有些时候,就像我在开头举的那个例子,业务逻辑中间要跳好几层,最终只想跳到入口界面,即栈中只需要存在一个相应的Activity
,而不是多个。
总结一个 singleTask 的作用,若一个Activity
的 launchMode 被指定为 singleTask,则在对应的栈中只会存在一个实例。若这个实例不存在,则新建实例;若存在,则把在栈中该实例之上的其他实例移除出栈,并调用该Activity
的onNewIntent()
方法,将 intent 传入。
我们来通过代码验证一下:启动了四个Activity
,
Run #7: ActivityRecord{b53e419 u0 com.air.activitylaunchmodetestdemo/.BActivity t3909}
Run #6: ActivityRecord{7a83deb u0 com.air.activitylaunchmodetestdemo/.AActivity t3909}
Run #5: ActivityRecord{df9c4ad u0 com.air.activitylaunchmodetestdemo/.CActivity t3909}
Run #4: ActivityRecord{e7c438 u0 com.air.activitylaunchmodetestdemo/.MainActivity t3909}
CActivity 为 launchMode 为 singleTask 的Activity
,此时,从 BActivity 启动 CActivity,通过前面的介绍,我们猜测会调用 CActivity 的onNewIntent()
方法,并且,在栈中,CActivity 之上的Activity
都会被移除,此时,执行startActivity()
方法,最新的栈内容如下:
Run #5: ActivityRecord{df9c4ad u0 com.air.activitylaunchmodetestdemo/.CActivity t3909}
Run #4: ActivityRecord{e7c438 u0 com.air.activitylaunchmodetestdemo/.MainActivity t3909}
可以看出,确实是我们所介绍的效果。至于设置 taskAffinity 后该 launchMode 的表现,我们在这篇文章中不会涉及,留到下一篇文章再与大家见面。
常用场景:注册 Activity 或者启动应用的首个Activity
,这样就不用担心任务栈内有其他的Activity
没有销毁了。
- “singleInstance”
这个或许可以称之为 singleTask 模式的加强版,对于设置了 singleTask 的Activity
,我们可以让它加入到在我们当前的任务栈中,也可以新建一个任务栈,把它放入(设置 taskAffinity 的情况下)。但是,对于 singleInstance 的Activity
,我们只能新建一个任务栈,并把它放入,没有任何Activity
可以和它在一个栈中。
那么对于我们开发的应用,其中有一个Activity
需要给其他应用使用,比如浏览器,你从自己的应用点开一个链接,使用浏览器应用开启网页,你单机后退,直接退到了你的应用,而不是你之前浏览的其他页面,如果你在浏览器中访问的连接可以访问你的应用,那么,此时点击后退你会发现,居然在你应用所在的栈中进行了回退操作,而不是退到浏览器页面,然后你退出应用,发现这个时候反倒打开了浏览器页面,这样说有点绕,我们以图的形势来展示一下。
至于常用场景,我本以为支付、分享之类的很可能会使用,但是我反编译微信查看其 manifest 文件的时候,发现它并没有使用这个 launchMode,所以暂时这里空着,如果你有什么使用场景,可以给我留言。
参考文章:
参考书籍:
- 《第一行代码》
- 《Android 开发艺术探索》