西西说
作为一只安卓小白,还是愿意和大家分享我的探索旅程,那么就让我们先来一点来自Android launchMode的温馨提示吧~(≧▽≦)/~。
背景知识
“现在我的手中有一摞牌,但是我让你看到的只有一张。”
“其他牌去哪了呢?”
“其他张牌都被最上面这张牌挡住了呀。”
上面的这段话,很形象地解释了acitivity和任务栈的关系。
接下来我们首先了解一下任务——Task。
Task
- Task 是activities的集合,通过back stack来管理,依靠先进后出队列来实现;
- 每个task中都至少有一个activity,新实例出来的activity置于栈顶
- Task可以被切换到后台
taskAffinity
- taskAffinity 这个属性主要是决定持有每个activity属于哪个task。
- 默认情况下,同一个包中的activity共享同一个affinity(任务共用性)。
launchMode
- 用来设定activity应该如何启动
- 主要有四种模式:
- standard(default)
- singleTop
- singleTask
- singleInstance
有没有需要改变默认设置的情况捏?
- 有多个入口指向app
- Launcher
- Notification
- Share Intent
- “单例”Activity
- 比如浏览器希望和很多个实例分享数据
- 有啥可以改变呢?
- activity的launchMode属性
- Intent的flag
而在以上情况下,我们的默认启动模式都是无法满足的。
那么问题来了。。。启动模式哪家强(=@__@=)?
接下来,就让我们走近Android的4种启动模式吧~
Standard
- 处于这种加载模式下的Activity可以有多个实例;
- 它的实例可以住在不同的task中;
- 一个task可以有多个它的实例
来,让我们一起做个实验吧
- 实验配置:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xixi.standardlaunchmode" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".ActivityA"
android:label="@string/app_name" />
<activity
android:name=".ActivityB"
android:taskAffinity="com.other" />
</application>
</manifest>
实验过程
实验探析:
- 怎么得出这个图的捏?
- 首先我们可以先查看log
- 接下来,我们再使用“adb shell dumpsys”来检测 Android的Activity任务栈

通过上图我们可以看到standard模式的两个activity均在每次启动时不断实例化,但是我们知道activityB的taskAffinity是“com.other”呀,为啥没有起作用捏?~ - 因为我们没有为intent添加参数:FLAG_ACTIVITY_NEW_TASK,加上以后会不会起作用捏?
日志是什么样的捏?
- 性能总结:
- 在Lollipop之前,每发出一个intent,对应的activity就会被创建出来,新建出来的activity会放在那个发出intent的activity相同的任务栈中(这里,我们不考虑taskAffinity不同);
- 在Lollipop版本时,来自相同application的activity,具体效果和之前一样,被创建出来就放在栈顶,但是如果是不同application的activity,被创建出来的activity会放在一个新的栈中(具体参见:http://inthecheesefactory.com/blog/understand-android-activity-launchmode/en)
SingleTop
- 本质上来说,这个模式同Standard模式没有太大区别,只有在一个特殊的情况时会有例外。
- 当back stack 的栈顶上已经存在这个模式activity的实例时,就不会被再实例,而是调用onNewIntent()来复用实例
来,让我们一起做个实验吧,演示如下: - 实验配置:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xixi.standardlaunchmode" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".ActivityA"
android:label="@string/app_name" />
<activity
android:name=".ActivityB"
android:taskAffinity="com.other" />
</application>
</manifest>
- 实验过程:
- 实验探析:
首先 我们依然先看下日志:
接下来,进一步查看下任务栈:
性能总结
- 基本与standard 相同,只有在任务栈中已存在时,则不需要重新创建,而是调用onNewIntent()
接下来,我们需要进入与前两种模式完全不一样的啦,稍事休息:
AV8D,R U ready?嘿儿未够!
SingleTask
对于这个磨人的“小妖精”,官方文档中是这么描述的:
The system creates a new task and instantiates the activity at the root of the new task.
However, if an instance of the activity already exists in a separate task,`
the system routes the intent to the existing instance through a call to its onNewIntent() method,rather than creating a new instance. Only one instance of the activity can exist at a time.
而事实真的是这样么 还是先做实验吧
实验1: A的启动模式设为singleTask 3个Activity均在同一task中
- 实验配置:
<activity android:name=".ActivityA" android:launchMode="singleTask" />
<activity android:name=".ActivityB" />
<activity android:name=".ActivityC" android:taskAffinity="com.others" />
- 实验过程记录
我们可以看到,再次启动A时,因为任务栈中已经存在了A的实例,所以为了把它提到栈顶来就把其他已经存在的Activity pop out了。 - 实验日志任务栈变化:
再次启动A后:
日志:
那么,如果B是singleTask A是standard会怎么样呢?
实验2: B的启动模式设为singleTask 3个Activity均在同一task中
- 实验配置:
<activity android:name=".ActivityA" />
<activity android:name=".ActivityB" android:launchMode="singleTask" />
<activity android:name=".ActivityC" android:taskAffinity="com.others" />
- 实验过程记录
任务栈记录
再次启动B后:
小结:
通过以上2个实验,我们可以看到:- 在同一个task(相当于在同一个app)中,若存在singleTask的activity,那么它会且只会被实例化1次。
- 如果栈中已经存在它的实例了,它会直接通过onNewIntent来调用它,并且会把栈中在它上面的其他实例销毁掉;
- 通过实验2我们可以知道,当singleTask Activity的实例不位于栈底,它是不会作为root Activity的,因为它并没有重新启动一个新的task,这一点说明官方文档与实际情况是有出入的。
实验3: A的启动模式设为singleTask 3个Activity 不在同一task中
实验配置:
与实验1相同;- 实验过程
再次启动A后:
- 实验过程
通过这个实验,我们发现ActivityA还在task 25中,并没有在新的task中实例化,并且再次启动它时,为了把它置于栈顶,原来在它上面的activity也被结束了。
- 性能总结
- Activity为singleTask启动模式的,有且只有1个实例;
- 若位于同一task中的activity想要再次启动它,则在stack中位于它上面的Activity将会被全部结束,从而使它位于任务栈的栈顶;
- 如果想要使它在新的task中启动,需要为它设置taskAffinity属性
SingleInstance
同SingleTask一样有且只有一个实例 那么区别在哪捏?
实验1: A的启动模式设为singleInstance 3个activity分别在不同的task中
- 实验配置:
<activity android:name=".ActivityA" android:launchMode="singleInstance" />
<activity android:name=".ActivityB" />
<activity android:name=".ActivityC" android:taskAffinity="com.others" />
- 实验过程:
实验探析
再次启动A后:那么日志是啥样的呢?
小结:
- singleInstance的activity是作为task的root acitivity,它所在的task中只能放它一个。
实验2: B的启动模式设为singleInstance 2个activity在不同的task中
- 实验配置:
<activity android:name=".ActivityA" />
<activity android:name=".ActivityB" android:launchMode="singleInstance" />
<activity android:name=".ActivityC" android:taskAffinity="com.others" />
实验过程
再次启动B后:
性能总结
- Activity为singleInstance启动模式的,系统中有且只有1个实例;
- 它是root Activity;
- 它比较孤僻,并且坚决不可以忍受和其他activity共用1个task;
- 简单来说:
foreach task in the tasks{ if(hasSingleInstanceActivity) { task.isUnique = true; task.activityCapacity = 1; singleInstance.isUnique = true; acitvity.isRootActivity = true; } }
总结
- Android launchMode:
- standard:activity有很多实例,不同实例还可以位于不同task中;
- singleTop:activity同standard基本相同,只有当栈顶已经有它的实例的时候,就不需要再实例化了;
- singleTask: activity有且只有1个实例,其它activity发出intent需要启动它时,和它处于同一栈中的activity就会被结束掉,从而保证它位于栈顶;
- singleInstance: 和singleTask很相像,但是它更为孤傲,也就是说整个系统中只存在一个存放它的task,这个task里也只存放它,而它也只有一个。
查阅资料
http://www.slideshare.net/RanNachmany/manipulating-android-tasks-and-back-stack
http://developer.android.com/guide/components/tasks-and-back-stack.html
http://inthecheesefactory.com/blog/understand-android-activity-launchmode/en
这里还有一个中文版哈Acitivity启动模式图文详解
http://blog.csdn.net/guolin_blog/article/details/41087993
http://blog.csdn.net/luoshengyang/article/details/6714543