前言
这里首先给大家推荐一篇文章吧,我转载的谷歌关于Activity的启动模式的官方文档。
其中对Activity的启动模式,任务和回退栈进行了较为详尽的分析。
但是有一些重点可能没有很清楚地描述出来,下面就来分析一下。当然前提条件是你基本了解谷歌关于Activity的启动模式的官方文档中的内容,任务,回退栈,启动模式,taskAffinity属性等。
taskAffinity
它是< activity >的一个重要属性,有以下特点:
1.taskAffinity指示当前 Activity 优先属于哪个任务。
2.在概念上,具有相同taskAffinity的activity(即设置了相同taskAffinity属性的activity)属于同一个任务。
3.这个属性决定两件事:
- 当activity被reparent时,它可以被reparent到哪个任务中。
- 当activity以FLAG_ACTIVITY_NEW_TASK标志启动时,它会被启动到哪个任务中。
4.默认情况下,一个应用中的所有activity具有相同的taskAffinity,即应用程序的包名。
我们可以通过设置不同的taskAffinity属性给应用中的activity分组,也可以把不同的应用中的activity的taskAffinity设置成相同的值。
5.只设置这一个属性没有作用,必须和Intent的Flag或者launchmode配合使用。
Intent.FLAG_ACTIVITY_NEW_TASK
从源码来看,Intent.FLAG_ACTIVITY_NEW_TASK是启动模式中最关键的一个Flag,依据该Flag启动模式可以分成两类,设置了该属性的与未设置该属性的。
对于非Activity启动的Activity(比如Service中启动的Activity)需要显示的设置Intent.FLAG_ACTIVITY_NEW_TASK,而singleTask及singleInstance在AMS中被预处理后,隐形的设置了Intent.FLAG_ACTIVITY_NEW_TASK。
而启动模式是standard及singletTop的Activity不会被设置Intent.FLAG_ACTIVITY_NEW_TASK,除非通过显示的intent setFlag进行设置。
FLAG_ACTIVITY_NEW_TASK这个属性更多的关注点是在Task,大多数情况下,需要将Activity引入到自己taskAffinity的Task中,Intent.FLAG_ACTIVITY_NEW_TASK的初衷是在Activity目标taskAffinity的Task中启动,非Activity(比如在Service中启动Activity)启动Activity都必须添加Intent.FLAG_ACTIVITY_NEW_TASK才行。
例子
假设我们现在要启动一个Activity TestActivity,它的taskAffinity值为com.tingshuonitiao.test,并且我们在启动的时候给Intent加上了Flag–Intent.FLAG_ACTIVITY_NEW_TASK。
那么在启动它的时候,我们会做如下分析:
如果存在com.tingshuonitiao.test这样的一个任务,则检查在这个任务中是否已经有了一个TestActivity的实例。
如果已经存在一个TestActivity的实例,并且它是栈的根Activity,启动该Activity的Intent和当前Intent相等,则会重用这个任务和任务中的TestActivity实例,将这个任务调到前台,清除位于TestActivity上面的所有Activity,显示TestActivity,并调用TestActivity的onNewIntent()。
如果不满足(1)已经存在一个TestActivity的实例,(2)并且它是栈的根Activity,(3)启动该Activity的Intent和当前intent相等这三个条件中的一条,就会在这个任务中创建TestActivity新的实例,并调用onCreate()。
如果不存在com.tingshuonitiao.test这样的一个任务,会创建一个新的taskAffinity为com.tingshuonitiao.test的任务,并且将TestActivity启动到这个新的任务中去。
以上所指的Intent相等,就是说两次启动Activity的Intent属性要完全一样。
假设我们要启动的TestActivity是一个根Activity,正常启动应用的时候它就是应用的入口,直接就打开了。在清单文件中有如下配置:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
那么我们现在在另一个任务中要去启动它就要给Intent加上两条对应的属性:
intent.setAction("android.intent.action.MAIN");
intent.addCategory("android.intent.category.LAUNCHER");
否则就不能满足Intent相等的要求,达不到所要的效果了。
singleTask
singleTask启动模式默认是加了Intent.FLAG_ACTIVITY_NEW_TASK这个Flag的,意味着可以在一个新的任务中开启,至于是不是真的会在新任务中开启,还要根据其他设置分析。
当然singleTask还有一点很重要的意思就是“single in task”,即在同一个任务中,只会有一个该Activity的实例。
例子
还是假设我们现在要启动一个Activity TestActivity,它的taskAffinity值为com.tingshuonitiao.test,并且它的launchmode是singleTask。
那么在启动它的时候,我们会做如下分析:
如果存在com.tingshuonitiao.test这样的一个任务,则检查在这个任务中是否已经有了一个TestActivity的实例。
如果已经存在一个TestActivity的实例,则会重用这个任务和任务中的TestActivity实例,将这个任务调到前台,清除位于TestActivity上面的所有Activity,显示TestActivity,并调用TestActivity的onNewIntent()。
如果不存在一个TestActivity的实例,会在这个任务中创建TestActivity的实例,并调用onCreate()。
如果不存在com.tingshuonitiao.test这样的一个任务,会创建一个新的taskAffinity为com.tingshuonitiao.test的任务,并且将TestActivity启动到这个新的任务中去。
allowTaskReparenting
使用的时候记得添加exported这一属性,它默认值是false,如果被设置为了false,那么这个Activity将只会被当前Application或者拥有同样user ID的Application的组件调用。
我们使用的时候修改为true,所以在清单文件中做如下配置:
android:allowTaskReparenting="true"
android:exported="true"
这样的话,就能成功地达到Reparenting的效果了。
以上就是很有可能混淆或者遗漏的一些重点。
本文的内容总结自我测试的demo和如下两篇文章,强烈推荐大家阅读,本文没涉及到的可以在其中找到:
1.Android中Activity四种启动模式和taskAffinity属性详解
2.Android面试官装逼失败之:Activity的启动模式