安卓是采用任务栈来管理activity的,默认情况下多次启动同一个activity系统会重复创建这个activity的实例,并把每个实例依次入栈。为了避免activity实例的重复创建、应付特殊的场景、安卓为activity设计了启动模式。
1、standard:
标准启动模式,系统默认。每次启动一个activity系统都会重新创建一个新的实例,不管这个ativity的实例是否已经存在。
2、singleTop:
栈顶复用模式,这种模式下如果新的activity已经位于任务栈的栈顶,那么此activity不会重新创建实例即onCreate、onStart不会回调,同时会走他的onNewIntent方法回调。如果activity的实例已经存在站内,不是位于栈顶,还是会重新创建activity的实例的。
3、singleTask
站内复用模式,这是一种单利模式,这种模式下,只要activity实例在栈中存在,那么多次启动此activity都不会重新创建此 activity的实例,多次启动时系统会调用onNewIntent方法。
具体理解:
ActivityA被设置为SingleTask启动模式,当系统收到开启A的请求后,系统会寻找是否存在A想要的任务栈:1、如果不存在就重新创建一个任务栈,然后创建A的实例,吧实例放入任务栈中。
2、如果存在A所需的任务栈,这时查找A是否在栈中有实例存在,如果有实例存在系统会把A调到任务栈栈顶,同时调用它的onNewIntent方法。如果实例不存在就创建A的实例,吧A压入栈。(具体栗子如下图)
4 、singleInstance:
单实例模式,具有singleTask的全部性质外,还加强了一点:具有此启动模式的activity只能单独存在一个任务栈中。换句话说比如ActivityA是singleInstance模式,当A启动后系统会为他创建一个新的任务栈,然后A独自运行在这个任务栈中,由于站内复用特性,后续的请求都不会创建新activity,除非这个独特的任务栈被系统销毁。
什么是activity所需的任务栈呢?这要从一个参数说起:TaskAffinity(任务相关性),这个参数标识了任务栈的名字。默认情况下所有的activity所需的任务栈名字都是应用包名。当然我们也可以单独为activity指定这个属性,但是属性值不能和包名相同,否则就相当于没有指定。
(1)TaskAffinity的使用
1、在manifest文件的activity标签下指定。
2、这个属性主要与SingleTask启动模式或者allowTaskReparenting属性配对使用其他情况下没有意义。
- taskAffinity 与 singleTask 配对使用:如果待启动的activity设置了这两个属性 ,这个 Activity就会在 taskAffinity设置的任务栈中。
- taskAffinity 与 allowTaskReparenting 配对使用:当一个应用 A 启动了应用 B 的某个 Activity 后,如果这个 Activity 的 allowTaskReparenting 属性为 true 的话,那么当应用 B 被启动后,此 Activity 会直接从应用 A 的任务栈转移到应用 B 的任务栈中。这个属性主要作用就是将这个 Activity 转移到它所属的任务栈中。这里不好理解栗子如下图和代码
3、默认情况下TaskAffinity属性值为包名。即sigleTask模式下任务栈默认为启动他的activity所在任务栈,当待启动activity的TaskAffinity与启动他的activity的TaskAffinity不同时,待启动的activity所在任务栈就不再是启动它的activity所在的任务栈了。
//应用A#MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btn.setOnClickListener {
// 开启应用B的ActivityC
val intent = Intent()
intent.action = Intent.ACTION_VIEW
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.addCategory("com.sunnyday.activitytask")
startActivity(intent)
}
}
}
//应用B的manifest
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<!--
1、设置allowTaskReparenting = true
2、 android:taskAffinity //默认为包名
-->
<activity android:name=".ActivityC"
android:exported="true"
android:allowTaskReparenting="true">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="com.sunnyday.activitytask"/>
</intent-filter>
</activity>
<activity
android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
(2) 任务栈分类:
- 前台任务栈:前台activity所处的任务栈即为前台任务栈。
- 后台任务栈:后台activity所处的任务栈即为后台任务栈。后台任务栈中activity处于暂停或者stop状态)
(3)前后台任务栈结合SingleTask模式栗子
假设目前两个任务栈,前台任务栈内有AB,后台任务栈有CD这里假设CD的启动模式均singleTask
1、现在请求D那么整个后台任务栈都会被切换到前台。如下如图一。
2、如果不是请求D而是请求C情况就不一样了。如下图二。
xml中activity节点配置(不配置默认为standard)
intent中设置标志位来为Activity指定启动模式(如下图)
二者区别:
1、优先级上代码配置高于xml配置,两种同时存在时以代码为准。
2、两种方式在限定范围上有所不同,第一种方式无法为activity设定Intent.FLAG_ACTIVITY_CLEAR_TOP 标识,第二种方式无法为activity指定singleInstance启动模式。
flag | 说明 |
---|---|
FLAG_ACTIVITY_NEW_TASK | 为activity指定singleTask启动模式,效果和在xml中指定的效果相同。 |
FLAG_ACTIVITY_SINGLE_TOP | 为 Activity 指定 singleTop 启动模式,其效果和在 xml 中指定启动模式相同 |
FLAG_ACTIVITY_CLEAR_TOP | 具有此标记位的 Activity,当启动它时,同一个任务栈中所有位于它上面的 Activity 都要出栈。这个模式一般需要和 FLAG_ACTIVITY_NEW_TASK 配合使用,在这种情况下,被启动的 Activity 实例如果已经存在,那么系统就会调用它的 onNewIntent。如果被启动的 Activity 采用 standard 模式启动,那么它连同它之上的 Activity 都要出栈,系统会创建新的 Activity 实例并放入栈顶SingleTask模式默认有这个flag。 |
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | 具有这个标记的 Activity 不会出现在历史的 Activity 的列表中,当某些情况下我们不希望用户通过历史列表回到我们的 Activity 的时候这个标记比较有用。它等同于在 xml 中指定 Activity 的属性 android:excludeFormRecents=“true” |
没探究安卓源码之前这里只需要明白:
1、TaskAffinity与SingleTask模式一起使用。场景是当你想要开启一个新的任务栈
2、allowTaskReparenting与TaskAffinity一起使用。场景如上应用A跳转用用B的ActivityC栗子。
ps:除了如上使用方式一般其他使用方式无意义。
正确,对的。
使用taskid获取,这个唯一标识。TaskAffinity也可获取单不是唯一标识,因为默认值都相同。这个值只是在singleTask模式下影响activity的taskid。
1、activity的方法
getTaskId() // 获取当前activity的taskid
2、ActivityInfo 的方法
fun dumpTaskAffinity() {
try {
val info = this.packageManager
.getActivityInfo(componentName, PackageManager.GET_META_DATA)
//获取activity的taskAffinity值
Log.i("TAG", "taskAffinity:" + info.taskAffinity)
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
}
}
3、查看所有activity的详细信息
adb shell dumpsys activity a