Tasks and Back Stack

一. 起因: 

遇到一个bug, 从应用A用intent调起应用B的两个Activity, B1和B2的时候, 会遇到如下bug:

二. 复现步骤:

启动A, A调起B1, home, 启动A, A调起B2, 启动A, A调起B1.

这样bug出现, 第二次调起B1的时候, 出现的是B2的界面. 以后一直调起B1, 出现的都是B2的界面.

发现在后面每次调起B1的时候会有log:

Class not found when unmarshalling: com.XXX.XXX

java.lang.ClassNotFoundException: com.XXX.XXX

at java.lang.Class.classForName(Native Method)

......

03-14 15:01:43.108  1897  4129 E Parcel  :... 19 more

03-14 15:01:43.108  1897  4129 E Parcel  : Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available

三. 解决办法: 

我在A启动B的时候, 将

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

改为用:

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

四. 原因

启动A单独作为一个task启动, 然后当A启动B1的时候, B1新建一个task, 包名命名com.B. 当A启动B2的时候, 需要放在另一个task中, 但是发现了此时有com.B (因为B1和B2是属于同一个app, 包名一样都是com.B, 会放在同一个task中). 因而在task com.B中又放了一个B2. 此时此task中有B1, B2. 栈顶是B2. 当A又试图发intent启动B1的时候, 由于是 FLAG_ACTIVITY_NEW_TASK格式的(类似singleTask), 因而只要B1的实例在com.B中存在, 就不会再实例化, 因而会将 com.B 这个task拿到前面, 但是此时这个task里的栈顶是B1. 因而会报错, 并且我们看到的界面是B2, 并且以后无论用同样方法启动B1多少次, 都看到的是B2.

改成加了Intent.FLAG_ACTIVITY_CLEAR_TOP后, A再次启动B1的时候, com.B这个task里会将在B1顶部的所有实例都销毁掉, 所以task里就只有B1了, 这时B1可以启动并且没有错误了, 看到的当前Activity也是B1.

五. 知识点

https://developer.android.com/guide/components/activities/tasks-and-back-stack.html

1. task: 

A task is a collection of activities that users interact with when performing a certain job. 

The device Home screen is the starting place for most tasks. When the user touches an icon in the application launcher (or a shortcut on the Home screen), that application's task comes to the foreground. If no task exists for the application (the application has not been used recently), then a new task is created and the "main" activity for that application opens as the root activity in the stack.

A task is a cohesive unit that can move to the "background" when users begin a new task or go to the Home screen, via the Home button.

通常, 用户从桌面的图标启动一个app, 就是一次task的开始. 

2. launch mode: 

Using the manifest file

When you declare an activity in your manifest file, you can specify how the activity should associate with tasks when it starts.

Using Intent flags

When you call startActivity(), you can include a flag in the Intent that declares how (or whether) the new activity should associate with the current task.

As such, if Activity A starts Activity B, Activity B can define in its manifest how it should associate with the current task (if at all) and Activity A can also request how Activity B should associate with current task. If both activities define how Activity B should associate with a task, then Activity A's request (as defined in the intent) is honored over Activity B's request (as defined in its manifest).

两种方法改变launch mode: 1 写在manifest里, 2 在intent中设置flag. 如果二者都设置了, 以intent中的flag为准.

3. manifest里的值:

standard(default): 创建新activity的实例放在当前task中, 并且一个activity可以实例化多次, 每个实例可能属于不同的task, 一个task里可能包含多个实例.

singleTop: 和standard一样, 特别的是: 如果当前task的栈顶是这个activity的实例, 那么就不新建实例了. 

For example, suppose a task's back stack consists of root activity A with activities B, C, and D on top (the stack is A-B-C-D; D is on top). An intent arrives for an activity of type D. If D has the default"standard"launch mode, a new instance of the class is launched and the stack becomes A-B-C-D-D. However, if D's launch mode is"singleTop", the existing instance of D receives the intent throughonNewIntent(), because it's at the top of the stack—the stack remains A-B-C-D. However, if an intent arrives for an activity of type B, then a new instance of B is added to the stack, even if its launch mode is"singleTop".

singleTask: The system creates a new task and instantiates the activity at the root of the new task. 但是, 如果这个activity的实例已经在其他task中存在了, 那么不会创建新的实例, 用已有的实例. 就是, 同一时间只能存在一个实例.

singleInstance: 和singleTask一样, 但是这个activity is always the single and only member of its task; any activities started by this one open in a separate task. 这个task中只有着一个activity

4. flag的值:

FLAG_ACTIVITY_NEW_TASK:   在新的task里开始一个Activity的实例. 如果task已经存在, 那么这个task被 brought to the foreground. 类似singleTask.

FLAG_ACTIVITY_SINGLE_TOP:  类似singleTop

FLAG_ACTIVITY_CLEAR_TOP: If the activity being started is already running in the current task, then instead of launching a new instance of that activity, all of the other activities on top of it are destroyed and this intent is delivered to the resumed instance of the activity (now on top), through onNewIntent()).

FLAG_ACTIVITY_CLEAR_TOPis most often used in conjunction withFLAG_ACTIVITY_NEW_TASK. When used together, these flags are a way of locating an existing activity in another task and putting it in a position where it can respond to the intent.



总结:

系统提供了两种切换: 

第一种是在AndroidManifest.xml文件中声明Activity自身的启动属性, 另一种是启动时给intent中添加不同的flag.

前者包括:

android:launchMode=standard/singleTop/singleTask/singleInstance

android:clearTaskOnLaunch=true/false

android:finishOnTaskLaunch=true/false

android:allowTaskReparent=true/false

后者包括:

Intent.FLAG_ACTIVITY_NEW_TASK

Intent.FLAG_ACTIVITY_RESER_TASK_IF_NEEDED

Intent.FLAG_ACTIVITY_CLEAR_TOP

Intent.FLAG_ACTIVITY_REORDER_TO_FRONT

Intent.FLAG_ACTIVITY_NO_HISTORY

Intent.FLAG_ACTIVITY_SINGLE_TOP

功能分为三类, 第一类是为了完成在Task之间切换,  第二类是完成在当前Task中改变Activity的顺序, 第三类是为了在Task切换时Task内部重排所属的Activity

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 原文地址:http://developer.android.com/guide/components/tasks-...
    l_genius阅读 3,770评论 0 0
  • 今天,学习一下六个帽子学习法,顾名思义,是指使用六种不同颜色的帽子代表不同的思维方式,这些思维方式任何人都有能力掌...
    snailwww阅读 6,934评论 0 4
  • 对不起。 不接受。 大概被伤害的人都是善良的吧,纠结于自己是不是太过冷漠,是不是应该为他考虑,又或者别人的做法有什...
    薄荷味彩铅阅读 1,594评论 0 0
  • 我和大多数人一样,经常用微博、空间、贴吧这种社交平台。但是后来又陆陆续续戒掉了——或者说是放弃了,因为它们完完全全...
    青山遥也阅读 2,958评论 5 3

友情链接更多精彩内容