Android 的任务栈 Task 与启动模式

2973次阅读  |  发布于4年以前

什么是任务

任务是用户在执行某项工作时与之互动的一系列 Activity 的集合。这些 Activity 按照每个 Activity 打开的顺序排列在一个返回堆栈中。

如何管理任务 

启动模式定义 Activity 的新实例如何与当前任务关联。可以借助 清单元素中的属性以及传递给 startActivity() 的 intent 中的标记来实现任务的关联方式。

如何查看当前系统的任务栈 

  1. 手机中点击多任务键 可以看到系统当前的任务栈。

  2. 命令行中  adb shell dumpsys activity  查看以下关键内容

...  
ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
...
   Running activities (most recent first):
   TaskRecord{386481f #782 A=com.testintent_1 U=0 StackId=1 sz=1}
     Run #1: ActivityRecord{e60b18e u0 com.testintent/.MainActivity t782}
   TaskRecord {120576c #781 A=com.testintent U=0 StackId=1 sz=1}
     Run #0: ActivityRecord{bdb842e u0 com.testintent/.CoverActivity t781}
...

有两个任务栈:com.testintent 和 com.testintent_1。

Activity 启动模式

1. standard 默认模式。系统在启动该 Activity 的任务中创建一个新实例,并将 intent 传送给该实例。Activity 可以多次实例化,每个实例可以属于不同的任务,一个任务可以拥有多个实例。

2. singleTop 如果当前任务的顶部已存在 Activity 的实例,则系统会通过调用其 onNewIntent() 方法来将 intent 转送给该实例,而不是创建 Activity 的新实例。Activity 可以多次实例化,每个实例可以属于不同的任务,一个任务可以拥有多个实例(但前提是返回堆栈顶部的 Activity 不是该 Activity 的现有实例)。

3.  singleTask 当一个具有 singleTask 模式的 Activity 请求启动,系统首先会寻找是否存在 Activity 想要的任务栈(taskAffinity 标识 Activity 所需的任务栈的名字,默认情况下是应用的包名),如果不存在,就创建一个新任务栈,然后创建 A 的实例放入栈中。如果存在所需任务栈,有实例存在,就把 A 上面的 Activity 全部出栈,并将 intent 传送给该实例的 onNewIntent() 方法,如果实例不存在,就创建实例并放入栈中。

再具体点:A  应用启动 B  应用的 ActivityC,然后按 home 键回到桌面,再单击 B 应用的图标,这时启动的不是 B 的主 Activity,而是重新显示了 A 应用启动的 ActivityC,C 从 A 的任务栈转移到了 B 的任务栈。

1. 4.x版本.会立刻在上个 activity 中 onActivityResult 中返回一个为 cancel 的 resultCode。(不管新的 activity 是否是在新的任务栈中启动) 。

2. 5.x版本.不管是否定义了 taskAffinity,都会把将要被启动的 activity 的启动模式忽略,onActivityResult 方法会正常回调。

4. singleInstance 与 "singleTask" 相似,唯一不同的是系统不会将任何其他 Activity 启动到包含该实例的任务中。该 Activity 始终是其任务唯一的成员;由该 Activity 启动的任何 Activity 都会在其他的任务中打开。

onNewIntent() 使用注意

方法体中需手动调用 setIntent(intent),否则之后的 getIntent() 获取的都是旧的 intent 对象。

常用的 Intent Flag

1. FLAG_ACTIVITY_NEW_TASK

...
 * If set, this activity will become the start of a new task on this history stack.
 ...
 * 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.
 * This flag can not be used when the caller is requesting a result from the activity being launched.
 ...

如果设置了,此活动将成为此历史堆栈上新任务的开始。当使用这个 flag 时,如果 task 中已经有了你要启动的 Activity ,就不再启动一个新的 Activity;当前 task 会被带到前台。可以用 FLAG_ACTIVITY_MULTIPLE_TASK 使这种行为无效。当调用者 startActivityForResult() 时,不能使用此标志。

比如栈中情况是 A,B,C,在 C 中启动 D,如果在 Manifest.xml 文件中给 D 添加了 Affinity(默认是包名) 的值和 C 所在的 Task 中的不一样,则会在新标记的 Affinity 所存在的 Task 中看 D 是否已经启动,如果已经启动直接将 D 所在的 task 带入到前台,否则直接将 activity 启动;如果是默认的或者指定的 Affinity 和 Task 一样,就和标准模式一样启动一个新的 Activity。此 flag 与启动模式 singleTask 效果不太一样,对于非 Activity 启动的 Activity(比如Service或者通知中启动的Activity)需要显示的设置 Intent.FLAG_ACTIVITY_NEW_TASK。

  1. FLAG_ACTIVITY_CLEAR_TOP
...
 * If it has declared its launch mode to be "multiple" (the
 * default) and you have not set {@link #FLAG_ACTIVITY_SINGLE_TOP} in
 * the same intent, then it will be finished and re-created; for all other
 * launch modes or if {@link #FLAG_ACTIVITY_SINGLE_TOP} is set then this
 * Intent will be delivered to the current instance's onNewIntent().
 ...

如果申明了 launch mode 是 "multiple" (默认情况就是)且没有在同一个 Intent 中设置 FLAG_ACTIVITY_SINGLE_TOP,Activity 将会结束且重新创建(回调 onCreate 生命周期方法);对于其他所有的 launch modes 或者设置了 FLAG_ACTIVITY_SINGLE_TOP,Intent 将被分发给实例的 onNewIntent() 方法。

比如栈中情况是 A,B,C,D,在 D 中启动 B(加入该flag), 栈中的情况将为 A,B,B 会执行 onCreate() ...。如果希望与 launch mode 中 singleTask 效果相同执行 onNewIntent(),可以同时加上 FLAG_ACTIVITY_SINGLE_TOP。

3. FLAG_ACTIVITY_SINGLE_TOP 


* If set, the activity will not be launched if it is already running`` 
* at the top of the history stack.

如果设置,如果此 activity 已经在历史堆栈的顶部将不会被启动。

相当于 launch mode 的 singleTop,比如栈中情况是 A,B,C,D,在 D 中启动D(加入该flag),栈中的情况还是 A,B,C,D。

4. FLAG_ACTIVITY_CLEAR_TASK

* 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}.

如果在一个 Intent 中设置,会导致在此 activity 开启之前,任何与该 activity 相关的 task 都会被清除。此 activity 将会是一个空 task 的最底部的 activity,之前所有的 activities 将被结束,此 flag 只能与 FLAG_ACTIVITY_NEW_TASK 配合使用。

5. FLAG_ACTIVITY_REORDER_TO_FRONT

* If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
 * this flag will cause the launched activity to be brought to the front of its
 * task's history stack if it is already running.
 * For example, consider a task consisting of four activities: A, B, C, D.
 * If D calls startActivity() with an Intent that resolves to the component
 * of activity B, then B will be brought to the front of the history stack,
 * with this resulting order:  A, C, D, B.
 * This flag will be ignored if {@link #FLAG_ACTIVITY_CLEAR_TOP} is also
 * specified.

如果在 Intent 中设置 ,如果待启动的 activity 已经开启在运行了,此 flag 将使其位于任务历史堆栈的前面。例如栈中情况是 A,B,C,D,如果 D 启动 B,栈中将变成A,C,D,B (B 将回调 onNewIntent() )。如果 FLAG_ACTIVITY_CLEAR_TOP 也被指定,此标志将被忽略。

6. FLAG_ACTIVITY_FORWARD_RESULT  

* If set and this intent is being used to launch a new activity from an
 * existing one, then the reply target of the existing activity will be
 * transferred to the new activity.  This way, the new activity can call
 * {@link android.app.Activity#setResult} and have that result sent back to
 * the reply target of the original activity.

如果在 Intent 中设置此 flag 从现有的 activity 去开启一个新的 activity ,现有的 activty 将会把回复的目标转移给新 activity. 新 activity 可以调用 setResult() 将结果发送给现有 activity 的回复目标。

例如:A 通过 startActivityForResult 启动 B,B 启动 C,但 B 为过渡页可以 finish 了,A 在期望 C 把结果返回。这种情况,B 可以在启动 C 的时候加入该flag。

7. FLAG_ACTIVITY_PREVIOUS_IS_TOP

* If set and this intent is being used to launch a new activity from an existing one
 ...
 * The previous activity will
 * be used as the top, with the assumption being that the current activity
 * will finish itself immediately.

如果当前的 activity 在开启新 activity 的 intent 设置此 flag, 当前 activity 之前的 activity 将被当视为 top,当前的 activity 将立即结束。

例如:栈中情况 A,B,C,C 启动 D 时使用此标志,在启动时 C 不会被当成栈顶 Activity,而是 B 作为栈顶启动 D,然后 C 会 finish()。经常与 FLAG_ACTIVITY_FORWARD_RESULT 一起配合使用。

8. FLAG_ACTIVITY_NO_HISTORY

* If set, the new activity is not kept in the history stack.  As soon as
* the user navigates away from it, the activity is finished.  This may also
* be set with the {@link android.R.styleable#AndroidManifestActivity_noHistory
* noHistory} attribute.
* If set, {@link android.app.Activity#onActivityResult onActivityResult()}
* is never invoked when the current activity starts a new activity which
* sets a result and finishes.

如果设置了此 flag,开启的 activity 将不会存在历史堆栈中。一旦用户离开它,activity 就结束了。也可以用{@link android.R来设置。styleable # AndroidManifestActivity_noHistory noHistory}属性。如果设置了此 flag,activity 将不会回调 onActivityResult()。

9. FLAG_ACTIVITY_TASK_ON_HOME  

Intent passed to {@link Context#startActivity Context.startActivity()},
* this flag will cause a newly launching task to be placed on top of the current
* home activity task (if there is one).  That is, pressing back from the task
* will always return the user to home even if that was not the last activity they
* saw. This can only be used in conjunction with {@link #FLAG_ACTIVITY_NEW_TASK}.

如果 intent 中设置此 flag 将使新启动的 task 置于当前 home activity 任务之上(如果有的话)。也就是说,从任务中返回总是会将用户返回到home,即使这不是他们看到的最后一个 activity。此 flag 只能与 FLAG_ACTIVITY_NEW_TASK 配合使用。

10. FLAG_EXCLUDE_STOPPED_PACKAGES   

* If set, this intent will not match any components in packages that
* are currently stopped.  If this is not set, then the default behavior
* is to include such applications in the result.

如果设置,intent 将与当前停止的包中的任何组件不匹配。如果未设置此flag,则默认行为是在结果中包含此类应用程序。

参考资料

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8