对于Android的启动模式,似乎是最简单的入门知识了,
Standard
、SingleTop
、SingleTask
、SingleInstance
。
本文重点对singleTask
进行加深理解。
入门及使用
任务栈(Task)
要了解启动模式的具体表现流程,首先要了解任务栈。官网介绍说任务与回退栈,这里直接成为任务栈。
一个Application
一般是由多个Activity
构成,而多个Activity
的管理,Android已经帮我们管理好了,使用栈方式管理,也就是先进后出,启动模式可以理解为四种不同的进栈出栈方式。而这个管理activity
的栈成为任务栈。
- 一个app默认只有一个任务栈,由系统指定。
- 一个app可以存在多个任务栈,需要自己手动设置。多个任务栈时,当前
activity
所在栈处于前台,其余栈处于后台。
启动模式
1. standard
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".A"
android:launchMode="standard"/>
standard
是android中默认的启动模式,如果你新建一个应用,不进行任何修改,那么MainActivity
的启动模式就是standard
。
standard
会进行重复创建实例,比如应用中有Main和A两个activity
,那么Main -> A -> A -> Main
这样的启动顺序,就会如下图所示:
点击back
返回,则会正常出栈,直到最后一个出栈,app
关闭回到桌面。
这种模式一般不推荐使用,会占用过多内存,页面重复。
2. singleTop
栈顶复用
<activity android:name=".A"
android:launchMode="singleTop"/>
只有这个activity
处于栈顶位置时,不会重新创建实例,而是会使用已经创建好的实例。并且会调用onNewIntent
方法,可以根据此方法获取intent
实例。
应用情景:
点击通知跳转到文章详情页,如果当前用户正在文章详情页查看时进行点击,那么可以使用singleTop
来确保不会重复创建实例,避免用户点击回退键时还是文章详情页。
3. singleTask
栈内只有一个实例
<activity android:name=".Main"
android:launchMode="singleTask"/>
singleTask
模式是栈内只允许存在一个实例,当这个实例已经创建,会直接使用这个实例,而不会重新创建,在这个实例上方的其他activity
也将全部出栈销毁。重用实例会调用onNewIntent
比如设置Main
为singleTask
,打开Main
后,再打开A,B,C三个默认standard
的Activity,由C打开Main
,则栈中只剩下Main
。
注意1:singleTask 和 taskAffinity
每个activity都有taskAffinity属性,如果没有显式的指明taskAffinity,那么就等于Application指明的taskAffinity,而如果Application也没有指明,那么该taskAffinity的值就等于应用的包名。
默认情况下singleTask所在的任务栈就是app启动的默认栈,而当设置了taskAffinity后,那么启动设置singleTask的activity,那么就会新创建一个栈来存放activity。
<activity android:name=".A"
android:launchMode="singleTask"
android:taskAffinity=".myNewTask"/>
这样一个app就存在两个任务栈。
启动流程是这样的:
a)判断当前任务栈taskAffinity
是否为.myNewTask
,如果是的话,直接将该activity置于当前任务栈栈顶
b)如果当前任务栈taskAffinity
不为.myNewTask
,则检查是否存在一个任务栈taskAffinity
值为.myNewTask
,如存在,则将该任务栈移到前台,并将该activity置于栈顶
c)如果并不存在一个任务栈taskAffinity
值为.myNewTask
,那么将会为该activity创建一个新的任务栈,该任务栈显示在前台
注意2:SingleTask && MAIN启动
每个app都会存在一个启动的activity
,即点击图标进入的第一个activity
。
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
当此时的MainActivity
设置android:launchMode="singleTask"
时,会产生这样一个现象:回退Home
后重新打开app,总是会重新打开,而不记录最后打开的页面。
这个其实就是由于栈内存在一个实例导致,因为之前已经启动过app,也就是后台存在这个app的任务栈,那么回退到Home再重新打开,首先会找到这个App对应的任务栈,而后由于启动的MainActvity
是SingleTask
,在任务栈中已经存在,那么会直接使用这个存在的实例,又因为MainActivity
处于栈底,那么启动这个已经存在的实例会将上方所有的activity
都清除出栈
注意3:allowTaskReparenting && taskAffinity
activity
的taskAffinity
,默认指向application
的taskAffinity
,默认是mainfest
里package
值,也就是常说的包名。
allowTaskReparenting
这个属性很有意思,可以将activity由一个task迁移到另一个task,而将activity
迁移到另一个task是又条件的:
- 首先指定
android:allowTaskReparenting="true"
- 指定
android:taskAffinity=".xx"
这个.xx
并不是你应用本身的taskAffinity
,或者不同应用默认taskAffinity
本身就不同,可以不指定taskAffinity
- 发生
Task Reset
,点击Home再启动APP可发生Task Reset
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.taskb">
<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">
<activity android:name=".OtherTaskActivity"
android:allowTaskReparenting="true"
android:taskAffinity=".othertask"/>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
taskAffinity
属性是迁移后的所指栈。
根据上述Mainfest
配置,执行流程:
- 首先点击APP图标启动
MainActivity
,创建一个taskAffinity
为com.example.taskb
的任务栈,MainActivity
入栈。
启动APP后栈状态 - 由
MainActivity
启动OtherTaskActivity
,那么他还是会在应用的栈中入栈。
启动OtherTaskActivity - 点击Home,发生
Task Reset
,应用com.example.taskb
任务栈处于后台,由于OtherTaskActivity
属性allowTaskReparenting="true" && taskAffinity=".othertask"
,所以此时这个应用后台将存在两个任务栈。
两个任务栈 - 点击APP图标重新启动应用,
com.example.taskb
任务栈处于前台,显示MainActivity
,com.example.taskb.othertask
任务栈处于后台,不显示。当按回退键时,com.example.taskb
任务栈清除,com.example.taskb.othertask
任务栈回到前台,显示OtherTaskActivity
。
注意4: taskAffinity起作用只有两种情况
- 设置
launchMode="singleTask"
或者intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- 设置
allowTaskReparenting=true
并满足前面所说的情况。
应用场景:
主activity
设置SingleTask
,退出应用清除栈。适合闪屏页+主页模式,闪屏页为MAIN
入口,跳转到主页前finish
闪屏页,主页启动模式为SingleTask
,可以实现保存应用状态。
4. singleInstance 栈内有且只有一个实例,并可复用
任务栈中只有一个activity
,如果存在,那么直接使用实例,调用onNewIntent
方法。