android:launcherMode的作用网上有很多介绍,也有大致的了解,但是最近在看源码时发现了一个android:documentLaunchMode,文档上说这个属性的作用也是用来控制Activity的启动的,那它和android:launcherMode有什么区别呢?里面为什么加个document?
上面给出的stackoverflow的答案解释的比较清楚,先把原文贴一下:
Let’s take a quick look at the
launchMode
values:
standard
andsingleTop
both allow multiple instances of an activity to be created, within other tasks.
singleTask
andsingleInstance
both limit an activity to a single instance, as the first activity in its task.Anything seem to be missing to you? None of these values allow multiple instances of an activity to be created at the top level. Either you launch instances of your activity into other people’s tasks, or you limit it to a single instance. None of these values allow multiple tasks to be created to host your activity. This oversight is what
documentLaunchMode
addresses. The idea is that if your activity has an intent filter that allows it to view documents, that each of those documents – each data uri – should be able to get its own instance of your activity in its own task.
standard和singleTop允许在一个stack中创建多个Activity的实例;singleTask和singleInstance限制只能有一个Activity的实例,那么如果我想启动两个一样的Activity,而且这两个Activity都作为task里的topActivity该怎么做呢?android:documentLaunchMode就是解决这个问题的,比如说,我有一个用来浏览文档的Activity,我想在浏览每个文档时都创建一个该Activity的实例,这样我可以在多个文档间切换,这个用android:documentLaunchMode就可以实现。
之所以注意到这个属性是因为测试反馈的一个问题:多次点击Google语音搜索的小部件,在最近任务中出现多个语音搜索的实例。第一感觉是Activity启动模式造成的,应该不是问题。但是以防万一还是抓了下Activity启动时的log:
1 | ❯❯❯ adb logcat | grep START |
从log来看Activity启动时Intent传递过来的flag是0x10008000,也就是FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK.先看下FLAG_ACTIVITY_NEW_TASK的注释:
1 | /** |
注释里提到,如果已经有stack里在运行要启动的Activity,那么不会再创建新的Activity,而是会把之前的Activity带到前台,类似于launcher的行为。再来看下FLAG_ACTIVITY_CLEAR_TASK的注释:
1 | /** |
如果启动Activity时带这个flag,那么和该Activity关联的stack会被清空,将新启动的Activity作为root。
从上面两个flag的注释来看,语音搜索启动的时候不应该有多个实例,而且还是位于不同的栈。然后就调试了下Activity的启动过程,发现ActivityStarter中启动Activity时的mLaunchFlags为403210240,转化成16进制为0x18088000,包含以下几个flag:
1 | FLAG_ACTIVITY_NEW_TASK |
第一个和第四个前面已经了解了,第三个调试过程中发现这个flag没起什么作用,这里就不关心了,可疑的就是FLAG_ACTIVITY_MULTIPLE_TASK了,来看下它的注释:
1 | /** |
从注释上看这个flag常和FLAG_ACTIVITY_NEW_DOCUMENT以及FLAG_ACTIVITY_NEW_TASK配合,并且会改变它们的行为,不管之前存不存在stack包含要启动的Activity,都会创建新的Stack来启动Activity。看来语音搜索有多个相同的实例,就是这个引起的了(其实现在看从名字上看就能推测出来,MULTI_TASK正好和现象一致)。
现在又有了一个新的问题,Activity启动时Intent携带的flag明明是0x10008000怎么变成0x18088000了呢?来看下ActivityStater中启动Activity的代码:
1 | private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord, |
从名字上看,似乎computeLaunchingTaskFlags()是用来计算flag的,但其实setInitialState()就改变了mLaunchFlag的值,而且查找FLAG_ACTIVITY_MULTIPLE_TASK的引用发现这个flag也是在setInitialState()方法中添加到mLaunchFlags中的。代码如下:
1 | private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask, |
从上面的代码可以看到在存在FLAG_ACTIVITY_NEW_TASK这个flag时如果mLaunchTaskBehind|| r.info.documentLaunchMode == DOCUMENT_LAUNCH_ALWAYS
为true,则会向mLaunchFlags中添加FLAG_ACTIVITY_MULTIPLE_TASK。而r.info.documentLaunchMode == DOCUMENT_LAUNCH_ALWAYS
对应的就是AndrodManifest.xml中的android:documentLaunchMode=”always”属性。