Android启动模式详解
Posted 一代小强
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android启动模式详解相关的知识,希望对你有一定的参考价值。
“在整理完启动模式后,我发现大家对启动模式的理解是有误区的“
引言
再谈启动模式,貌似没啥意思。但是你能正确回答下面的问题吗?
- 问题1:singleTask启动模式,在启动新的Activity的时候,真的会重新创建新的任务栈吗,所谓的任务栈存在指的是什么?
- 问题2:设置了Intent.FLAG_ACTIVITY_NEW_TASK,每次都会创建新的任务栈吗?
- 问题3:对于singleInstance,显式指定taskAffinity为应用包名,那在启动的时候,还会创建新的任务栈吗?
以下实验基于android 9.0。如果大家只是想了解每种启动模式的基本概念,可以只看对应的任务栈图,直接忽略掉每一小点。
笔者会用adb的打印,看每一种启动模式下,任务栈的变化。
一、ActivityRecord、TaskRecord、ActivityStack的区别
在回答上面的问题前,我们先整理下ActivityRecord、TaskRecord、ActivityStack的区别。
-
ActivityRecord
:一个ActivityRecord对应一个Activity,保存了Activity的所有信息;但一个Activity可能会有多个ActivityRecord,因为Activity可以被多次启动,这个主要取决于其启动模式。 -
TaskRecord
:一个TaskRecord由一个或者多个ActivityRecord组成,这就是我们常说的任务栈,具有后进先出的特点。 -
ActivityStack
:ActivityStack是用来管理TaskRecord的,包含了多个TaskRecord。
这里简单贴一下代码,让读者对这些概念有一些了解
/**
* State and management of a single stack of activities.
*/
class ActivityStack extends ConfigurationContainer
/**
* The back history of all previous (and possibly still
* running) activities. It contains #TaskRecord objects.
*/
private final ArrayList<TaskRecord> mTaskHistory = new ArrayList<>();
//..... 省略代码
class TaskRecord extends ConfigurationContainer
/**
*List of all activities in the task arranged in history order
*/
final ArrayList<ActivityRecord> mActivities;
//..... 省略代码
/**
* An entry in the history stack, representing an activity.
*/
final class ActivityRecord extends ConfigurationContainer
final ActivityInfo info; // all about me
int launchMode; // the launch mode activity attribute.
final String launchedFromPackage; // always the package who started the activity.
//..... 省略代码
启动模式的实际值
如果大家看了上面的代码,就会发现launchMode的值是int类型,与我们认知的不太一样,这里贴一下映射关系。
public class ActivityInfo extends ComponentInfo implements Parcelable
/**
* Constant corresponding to <code>standard</code> in
* the @link android.R.attr#launchMode attribute.
*/
public static final int LAUNCH_MULTIPLE = 0;
/**
* Constant corresponding to <code>singleTop</code> in
* the @link android.R.attr#launchMode attribute.
*/
public static final int LAUNCH_SINGLE_TOP = 1;
/**
* Constant corresponding to <code>singleTask</code> in
* the @link android.R.attr#launchMode attribute.
*/
public static final int LAUNCH_SINGLE_TASK = 2;
/**
* Constant corresponding to <code>singleInstance</code> in
* the @link android.R.attr#launchMode attribute.
*/
public static final int LAUNCH_SINGLE_INSTANCE = 3;
public String taskAffinity;
我们注意到ActivityInfo里面有一个taskAffinity字段,想必大家应该知道其作用了——指定任务栈名。不知道也没关系,后面会结合启动模式讲解。
使用dumpsys 命令查看任务栈
我们先来看看在launcher下,任务栈的情况
在cmd下运行 adb shell dumpsys activity a
可以得到如下信息
ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
Display #0 (activities from top to bottom):
Stack #0: type=home mode=fullscreen
isSleeping=false
mBounds=Rect(0, 0 - 0, 0)
Task id #1
mBounds=Rect(0, 0 - 0, 0)
mMinWidth=-1
mMinHeight=-1
mLastNonFullscreenBounds=null
* TaskRecorda9becdf #1 I=com.miui.home/.launcher.Launcher U=0 StackId=0 sz=1
userId=0 effectiveUid=u0a23 mCallingUid=1000 mUserSetupComplete=true mCallingPackage=com.android.systemui
intent=act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10800100 cmp=com.miui.home/.launcher.Launcher
realActivity=com.miui.home/.launcher.Launcher
autoRemoveRecents=false isPersistable=false numFullscreen=1 activityType=2
rootWasReset=false mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLE
Activities=[ActivityRecord3705f28 u0 com.miui.home/.launcher.Launcher t1]
askedCompatMode=false inRecents=true isAvailable=true
stackId=0
* Hist #0: ActivityRecord3705f28 u0 com.miui.home/.launcher.Launcher t1
packageName=com.miui.home processName=com.miui.home
launchedFromUid=0 launchedFromPackage=null userId=0
app=ProcessRecord2a0244b 2458:com.miui.home/u0a23
Intent act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10800100 cmp=com.miui.home/.launcher.Launcher
frontOfTask=true task=TaskRecorda9becdf #1 I=com.miui.home/.launcher.Launcher U=0 StackId=0 sz=1
taskAffinity=null
fullscreen=true noDisplay=false immersive=false launchMode=2
- Stack #0:即home下的ActivityStack id为0。
- Task id #1:即ActivityStack中有一个id为1的TaskRecord。
- Activities:用来存放ActivityRecord的,任务栈存放Activity的位置,可以通过这个关键字查看Activity的入栈出栈情况。“com.miui.home/.launcher.Launcher” 就是对应Activity的类路径。
- taskAffinity:细心的读者应该会注意到这个字段,就是我们在manifest文件中配置的,这里为null表明没有添加taskAffinity。
- launchMode:即启动模式,对应值为int。
二、Standard模式
默认值。系统在启动该 Activity 的任务中创建 Activity 的新实例,并将 intent 传送给该实例。Activity 可以多次实例化,每个实例可以属于不同的任务,一个任务可以拥有多个实例。
比如 A启动B,B启动A的任务栈变化情况如下
新建一个项目,MainActivity启动 SecondActivity,都是标准的启动模式。对应的Activity配置如下
<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=".StandardActivity"/>
1)intent不设置flag,不配置taskAffinity
新建一个项目,MainActivity启动 SecondActivity,都是标准的启动模式。打印的任务栈如下:
Stack #9: type=standard mode=fullscreen
isSleeping=false
mBounds=Rect(0, 0 - 0, 0)
Task id #18359
mBounds=Rect(0, 0 - 0, 0)
mMinWidth=-1
mMinHeight=-1
mLastNonFullscreenBounds=null
* TaskRecordb751d5b #18359 A=com.example.launchmode U=0 StackId=31 sz=2
affinity=com.example.launchmode
....
Activities=[ActivityRecord8b23942 u0 com.example.launchmode/.MainActivity t18359, ActivityRecordd0a45e5 u0 com.example.launchmode/.StandardActivity t18359]
....
可以看到Activities 里面有两个ActivityRecord,分别对应MainActivity,SecondActivity。这里贴一下对于的生命周期(注意,面试必考题)和TaskId,可以看到Task Id是一样的:
04-18 10:53:37.981 15379 15379 D MainActivity: onCreate: Task id 18359
04-18 10:53:38.029 15379 15379 D MainActivity: onStart: Task id 18359
04-18 10:53:38.032 15379 15379 D MainActivity: onResume: Task id 18359
04-18 10:53:39.611 15379 15379 D MainActivity: ------------- start activity -------------
04-18 10:53:39.622 15379 15379 D MainActivity: onPause: Task id 18359
04-18 10:53:39.635 15379 15379 D StandardActivity: onCreate: Task id 18359
04-18 10:53:39.649 15379 15379 D StandardActivity: onStart: Task id 18359
04-18 10:53:39.651 15379 15379 D StandardActivity: onResume: Task id 18359
04-18 10:53:40.199 15379 15379 D MainActivity: onStop: Task id 18359
Activity里面可以通过 getTaskId()
方式获取到TaskId,具体实现就不再讲解,作为一个课后作业。
2)只设置intent为FLAG_ACTIVITY_NEW_TASK
在MainActivity启动StandardActivity的时候,设置flag如下
public void next(View view)
Log.d(TAG, "------------- start activity -------------");
Intent intent = new Intent(this, StandardActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
对应的生命周期和TaskId如下,可见并没有启动新的任务栈
04-18 10:57:12.584 15563 15563 D MainActivity: onCreate: Task id 18361
04-18 10:57:12.631 15563 15563 D MainActivity: onStart: Task id 18361
04-18 10:57:12.634 15563 15563 D MainActivity: onResume: Task id 18361
04-18 10:57:14.096 15563 15563 D MainActivity: ------------- start activity -------------
04-18 10:57:14.105 15563 15563 D MainActivity: onPause: Task id 18361
04-18 10:57:14.117 15563 15563 D StandardActivity: onCreate: Task id 18361
04-18 10:57:14.132 15563 15563 D StandardActivity: onStart: Task id 18361
04-18 10:57:14.134 15563 15563 D StandardActivity: onResume: Task id 18361
04-18 10:57:14.677 15563 15563 D MainActivity: onStop: Task id 18361
3)只配置taskAffinity
在manifest中配置StandardActivity的taskAffinity为 com.demo.launchmode
<activity
android:name=".StandardActivity"
android:taskAffinity="com.demo.launchmode"/>
直接启动standardActivity,对应的周期和TaskId如下,可见这种方式也不会启动新的任务栈。
04-18 11:02:51.802 16954 16954 D MainActivity: onCreate: Task id 18363
04-18 11:02:51.849 16954 16954 D MainActivity: onStart: Task id 18363
04-18 11:02:51.852 16954 16954 D MainActivity: onResume: Task id 18363
04-18 11:02:53.283 16954 16954 D MainActivity: ------------- start activity -------------
04-18 11:02:53.293 16954 16954 D MainActivity: onPause: Task id 18363
04-18 11:02:53.306 16954 16954 D StandardActivity: onCreate: Task id 18363
04-18 11:02:53.321 16954 16954 D StandardActivity: onStart: Task id 18363
04-18 11:02:53.323 16954 16954 D StandardActivity: onResume: Task id 18363
04-18 11:02:53.873 16954 16954 D MainActivity: onStop: Task id 18363
4)intent设置FLAG_ACTIVITY_NEW_TASK 、配置taskAffinity
按照2)、3)方式,同时在intent设置FLAG_ACTIVITY_NEW_TASK 和配置taskAffinity。MainActivity与standardActivity的TaskId就不一样了。对应的栈结构,可以通过上面的dumpsys命令查看,这里就不列出来了。
04-18 11:04:55.107 17185 17185 D MainActivity: onCreate: Task id 18365
04-18 11:04:55.154 17185 17185 D MainActivity: onStart: Task id 18365
04-18 11:04:55.156 17185 17185 D MainActivity: onResume: Task id 18365
04-18 11:04:56.707 17185 17185 D MainActivity: ------------- start activity -------------
04-18 11:04:56.721 17185 17185 D MainActivity: onPause: Task id 18365
04-18 11:04:56.751 17185 17185 D StandardActivity: onCreate: Task id 18366
04-18 11:04:56.774 17185 17185 D StandardActivity: onStart: Task id 18366
04-18 11:04:56.776 17185 17185 D StandardActivity: onResume: Task id 18366
04-18 11:04:57.560 17185 17185 D MainActivity: onStop: Task id 18365
画重点:要同时指定taskAffinity和FLAG_ACTIVITY_NEW_TASK才能在新的任务栈启动Standard的Activity
三、SingleTop模式
如果当前任务的顶部已存在 Activity 的实例,则系统会通过调用其
onNewIntent()
方法来将 intent 转送给该实例,而不是创建 Activity 的新实例。Activity 可以多次实例化,每个实例可以属于不同的任务,一个任务可以拥有多个实例(但前提是返回堆栈顶部的 Activity 不是该 Activity 的现有实例)。
如果A以Standard启动B,B再以SingleTop启动B,对应的图为:
如果A以Standard启动B,B再以SingleTop启动A,对应的图为:
对应的Activity配置如下
<activity
android:name=".SingleTopActivity"
android:launchMode="singleTop" />
1)intent不设置flag,不配置taskAffinity
对应的周期是,这种情况是不会创建新的任务栈
04-18 16:40:43.267 24395 24395 D MainActivity: onCreate: Task id 18428
04-18 16:40:43.315 24395 24395 D MainActivity: onStart: Task id 18428
04-18 16:40:43.318 24395 24395 D MainActivity: onResume: Task id 18428
04-18 16:40:59.145 24395 24395 D MainActivity: ------------- start activity -------------
04-18 16:40:59.168 24395 24395 D MainActivity: onPause: Task id 18428
04-18 16:40:59.180 24395 24395 D SingleTopActivity: onCreate: Task id 18428
04-18 16:40:59.196 24395 24395 D SingleTopActivity: onStart: Task id 18428
04-18 16:40:59.198 24395 24395 D SingleTopActivity: onResume: Task id 18428
04-18 16:40:59.747 24395 24395 D MainActivity: onStop: Task id 18428
2)只设置intent为FLAG_ACTIVITY_NEW_TASK
对应的周期和TaskId如下,可见这种方式不会启动新的任务栈。
04-18 10:28:48.458 14065 14065 D MainActivity: onCreate: Task id 18343
04-18 10:28:48.505 14065 14065 D MainActivity: onStart: Task id 18343
04-18 10:28:48.508 14065 14065 D MainActivity: onResume: Task id 18343
04-18 10:28:50.358 14065 14065 D MainActivity: ------------- start activity -------------
04-18 10:28:50.367 14065 14065 D MainActivity: onPause: Task id 18343
04-18 10:28:50.380 14065 14065 D SingleTopActivity: onCreate: Task id 18343
04-18 10:28:50.394 14065 14065 D SingleTopActivity: onStart: Task id 18343
04-18 10:28:50.396 14065 14065 D SingleTopActivity: onResume: Task id 18343
04-18 10:28:50.939 14065 14065 D MainActivity: onStop: Task id 18343
3)只配置taskAffinity
对应的周期和TaskId如下,可见这种方式也不会启动新的任务栈。
04-18 10:35:29.907 14560 14560 D MainActivity: onCreate: Task id 18349
04-18 10:35:29.954 14560 14560 D MainActivity: onStart: Task id 18349
04-18 10:35:29.957 14560 14560 D MainActivity: onResume: Task id 18349
04-18 10:35:32.096 14560 14560 D MainActivity: ------------- start activity -------------
04-18 10:35:32.107 14560 14560 D MainActivity: onPause: Task id 18349
04-18 10:35:32.118 14560 14560 D SingleTopActivity: onCreate: Task id 18349
04-18 10:35:32.132 14560 14560 D SingleTopActivity: onStart: Task id 18349
04-18 10:35:32.134 14560 14560 D SingleTopActivity: onResume: Task id 18349
04-18 10:35:32.699 14560 14560 D MainActivity: onStop: Task id 18349
4)intent设置FLAG_ACTIVITY_NEW_TASK 、配置taskAffinity
周期、taskId如下,可以看到,singleTop的taskid已经变了。即这种方式可以启动新的任务栈。
04-18 10:31:35.382 14357 14357 D MainActivity: onCreate: Task id 18345
04-18 10:31:35.430 14357 14357 D MainActivity: onStart: Task id 18345
04-18 10:31:35.432 14357 14357 D MainActivity: onResume: Task id 18345
04-18 10:31:37.143 14357 14357 D MainActivity: ------------- start activity -------------
04-18 10:31:37.158 14357 14357 D MainActivity: onPause: Task id 18345
04-18 10:31:37.184 14357 14357 D SingleTopActivity: onCreate: Task id 18346
04-18 10:31:37.199 14357 14357 D SingleTopActivity: onStart: Task id 18346
04-18 10:31:37.201 14357 14357 D SingleTopActivity: onResume: Task id 18346
04-18 10:31:38.004 14357 14357 D MainActivity: onStop: Task id 18345
5)栈顶情况
在栈顶的时候,singletop的周期如下。可以看到会先调用onPause,然后才调用onNewIntent,onResume也会重新调用。
04-18 10:21:18.128 13663 13663 D SingleTopActivity: onCreate: Task id 18338
04以上是关于Android启动模式详解的主要内容,如果未能解决你的问题,请参考以下文章