android:documentLaunchMode属性的作用

参考:https://stackoverflow.com/questions/32154059/what-is-the-difference-between-androidlaunchmode-and-androiddocumentlaunchmode

android:launcherMode的作用网上有很多介绍,也有大致的了解,但是最近在看源码时发现了一个android:documentLaunchMode,文档上说这个属性的作用也是用来控制Activity的启动的,那它和android:launcherMode有什么区别呢?里面为什么加个document?

上面给出的stackoverflow的答案解释的比较清楚,先把原文贴一下:

Let’s take a quick look at the launchMode values:

standard and singleTop both allow multiple instances of an activity to be created, within other tasks.

singleTask and singleInstance 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
2
3
4
❯❯❯ adb logcat | grep START
11-16 16:25:48.432 893 984 I ActivityManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10200000 cmp=com.android.launcher3/.Launcher (has extras)} from uid 1000
11-16 16:25:50.403 893 8188 I ActivityManager: START u0 {act=com.google.android.apps.searchlite.WIDGET_ACTION flg=0x10008000 cmp=com.google.android.apps.searchlite/.ui.SearchActivity bnds=[302,106][338,155] (has extras)} from uid 10024

从log来看Activity启动时Intent传递过来的flag是0x10008000,也就是FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK.先看下FLAG_ACTIVITY_NEW_TASK的注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* If set, this activity will become the start of a new task on this
* history stack. A task (from the activity that started it to the
* next task activity) defines an atomic group of activities that the
* user can move to. Tasks can be moved to the foreground and background;
* all of the activities inside of a particular task always remain in
* the same order. See
* <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
* Stack</a> for more information about tasks.
*
* <p>This flag is generally used by activities that want
* to present a "launcher" style behavior: they give the user a list of
* separate things that can be done, which otherwise run completely
* independently of the activity launching them.
*
* <p>When using this flag, if a task is already running for the activity
* you are now starting, then a new activity will not be started; instead,
* the current task will simply be brought to the front of the screen with
* the state it was last in. See {@link #FLAG_ACTIVITY_MULTIPLE_TASK} for a flag
* to disable this behavior.
*
* <p>This flag can not be used when the caller is requesting a result from
* the activity being launched.
*/
public static final int FLAG_ACTIVITY_NEW_TASK = 0x10000000;

注释里提到,如果已经有stack里在运行要启动的Activity,那么不会再创建新的Activity,而是会把之前的Activity带到前台,类似于launcher的行为。再来看下FLAG_ACTIVITY_CLEAR_TASK的注释:

1
2
3
4
5
6
7
8
/**
* If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
* this flag will cause any existing task that would be associated with the
* activity to be cleared before the activity is started. That is, the activity
* becomes the new root of an otherwise empty task, and any old activities
* are finished. This can only be used in conjunction with {@link #FLAG_ACTIVITY_NEW_TASK}.
*/
public static final int FLAG_ACTIVITY_CLEAR_TASK = 0X00008000;

如果启动Activity时带这个flag,那么和该Activity关联的stack会被清空,将新启动的Activity作为root。

从上面两个flag的注释来看,语音搜索启动的时候不应该有多个实例,而且还是位于不同的栈。然后就调试了下Activity的启动过程,发现ActivityStarter中启动Activity时的mLaunchFlags为403210240,转化成16进制为0x18088000,包含以下几个flag:

1
2
3
4
FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_MULTIPLE_TASK
FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
FLAG_ACTIVITY_CLEAR_TASK

第一个和第四个前面已经了解了,第三个调试过程中发现这个flag没起什么作用,这里就不关心了,可疑的就是FLAG_ACTIVITY_MULTIPLE_TASK了,来看下它的注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* This flag is used to create a new task and launch an activity into it.
* This flag is always paired with either {@link #FLAG_ACTIVITY_NEW_DOCUMENT}
* or {@link #FLAG_ACTIVITY_NEW_TASK}. In both cases these flags alone would
* search through existing tasks for ones matching this Intent. Only if no such
* task is found would a new task be created. When paired with
* FLAG_ACTIVITY_MULTIPLE_TASK both of these behaviors are modified to skip
* the search for a matching task and unconditionally start a new task.
*
* <strong>When used with {@link #FLAG_ACTIVITY_NEW_TASK} do not use this
* flag unless you are implementing your own
* top-level application launcher.</strong> Used in conjunction with
* {@link #FLAG_ACTIVITY_NEW_TASK} to disable the
* behavior of bringing an existing task to the foreground. When set,
* a new task is <em>always</em> started to host the Activity for the
* Intent, regardless of whether there is already an existing task running
* the same thing.
*
* <p><strong>Because the default system does not include graphical task management,
* you should not use this flag unless you provide some way for a user to
* return back to the tasks you have launched.</strong>
*
* See {@link #FLAG_ACTIVITY_NEW_DOCUMENT} for details of this flag's use for
* creating new document tasks.
*
* <p>This flag is ignored if one of {@link #FLAG_ACTIVITY_NEW_TASK} or
* {@link #FLAG_ACTIVITY_NEW_DOCUMENT} is not also set.
*
* <p>See
* <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
* Stack</a> for more information about tasks.
*
* @see #FLAG_ACTIVITY_NEW_DOCUMENT
* @see #FLAG_ACTIVITY_NEW_TASK
*/
public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 0x08000000;

从注释上看这个flag常和FLAG_ACTIVITY_NEW_DOCUMENT以及FLAG_ACTIVITY_NEW_TASK配合,并且会改变它们的行为,不管之前存不存在stack包含要启动的Activity,都会创建新的Stack来启动Activity。看来语音搜索有多个相同的实例,就是这个引起的了(其实现在看从名字上看就能推测出来,MULTI_TASK正好和现象一致)。

现在又有了一个新的问题,Activity启动时Intent携带的flag明明是0x10008000怎么变成0x18088000了呢?来看下ActivityStater中启动Activity的代码:

1
2
3
4
5
6
7
8
9
10
11
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
ActivityRecord[] outActivity) {

setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor);

computeLaunchingTaskFlags();
//……
}

从名字上看,似乎computeLaunchingTaskFlags()是用来计算flag的,但其实setInitialState()就改变了mLaunchFlag的值,而且查找FLAG_ACTIVITY_MULTIPLE_TASK的引用发现这个flag也是在setInitialState()方法中添加到mLaunchFlags中的。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
boolean doResume, int startFlags, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
//……
// If we are actually going to launch in to a new task, there are some cases where
// we further want to do multiple task.
if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
if (mLaunchTaskBehind
|| r.info.documentLaunchMode == DOCUMENT_LAUNCH_ALWAYS) {
mLaunchFlags |= FLAG_ACTIVITY_MULTIPLE_TASK;
}
}
//……
}

从上面的代码可以看到在存在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”属性。

Powered by Hexo and Hexo-theme-hiker

Copyright © 2018 - 2022 得一 All Rights Reserved.

访客数 : | 访问量 :