目录
- 1 Activity 的生命周期
- 1.1 Activity 的生命周期分为哪两部分?
- 1.2 Activity 的状态有几种?
- 1.3 Activity 的生命周期方法有哪些?
- 1.4 正常情况下,Activity 的生命周期切换过程是怎样的?
- 1.5 onStart 和 onResume、onPause 和 onStop 的区别是什么?
- 1.6 假设当前 Activity 为 A,如果这时用户打开一个新的 Activity B,那么 B 的 onResume 和 A 的 onPause 哪个先执行呢?
- 1.7 异常情况下,Activity 的生命周期过程是怎样的?
- 1.8 onSaveInstanceState 什么情况下会被调用?
- 1.9 onSaveInstanceState 和 onRestoreInstanceState 在生命周期中的调用时序是什么?
- 1.10 onCreate 和 onRestoreInstanceState 恢复数据时的区别是什么?
- 1.11 onCreate 和 onRestart 的区别是什么?
- 1.12 当 Activity 在异常情况下需要重新创建时,系统是如何保存和恢复 View 层次结构的?
- 1.13 如何使得系统配置改变后,Activity 不会被重新创建?
- 相关面试题
- 2 Activity 的启动模式
- 2.1 Activity 的四种启动模式对比?
- 2.2 指定 Activity 启动模式的方法及其区别是什么?
- 2.3 查看任务栈的命令是什么?
- 2.4 有 3 个 Activity:A、B 和 C。其中 A 的启动模式是 standard,taskAffinity 是默认的,B 和 C 的启动模式是 singleTask,taskAffinity 不是默认的。进行如下操作,在 A 中单击按钮启动 B, 在 B 中单击按钮启动 C,在 C 中单击按钮启动 A,最后在 A 中单击按钮启动 B,现在按 2 次 back 键,然后看到的是哪个 Activity?
- 2.5 Activity 的 Flags
- 2.6 当调用startActivity()方法来启动一个Activity时,如果在Intent中加入了一个FLAG_ACTIVITY_NEW_TASK flag的话(或者该Activity在manifest文件中声明的启动模式是"singleTask"),一定会开始一个新的任务栈吗?
- 2.7 `android:taskAffinity` 是什么?在什么情况下起作用?
- 2.8 有 3 个 Activity:A,B 和 C. A 和 C 的启动模式都是 standard,B 的启动模式是 singleInstance。现在 A 打开 B,B 打开 C,那么 C 是处于 A 的任务栈中还是 B 的任务栈中?点击 back 键的退出顺序是什么样子的?
- 3 IntentFilter 的匹配规则
- 3.1 启动 Activity 的两种方式
- 3.2 action 的匹配规则
- 3.3 category 的匹配规则
- 3.4 data 的匹配规则
- 3.5 为什么隐式调用时,必须在 intent-filter 中指定 “android.intent.category.DEFAULT” 这个 catogery?
- 3.6 如何实现指定分享功能?比如,手机上可以分享到微信,qq,wps 等,现在要求如果有微信应用可以分享,直接分享到微信;否则,采用选择器分享。
- 3.7 为什么通过隐式启动 Activity 时,使用 PackageManager 的 resolveActivity 或者 queryIntentActivities 方法时,第二个参数要传递 PackageManager.MATCH_DEFAULT_ONLY?
- 3.8 如何获取应用注册的所有 Activity 和 Receiver?
- 参考
1 Activity 的生命周期
1.1 Activity 的生命周期分为哪两部分?
一部分是典型情况下的生命周期,另一部分是异常情况下的生命周期。典型情况下的生命周期,是指在有用户参与的情况下,Activity 所经过的生命周期的改变;而异常情况下的生命周期是指 Activity 被系统回收或者由于当前设备的 Configuration 发生改变从而导致 Activity 被销毁重建,异常情况下的生命周期的关注点和典型情况下略有不同。
1.2 Activity 的状态有几种?
每个 Activity
在生命周期中最多可能有 6 种状态:
- 创建状态(Created state):瞬时状态;
- 开始状态(Started state):瞬时状态;
- 运行状态(Resumed state):驻留状态,当一个
Activity
位于返回栈的栈顶时, 这时Activity
就处于运行状态; - 暂停状态(Paused state):驻留状态,当一个
Activity
不位于栈顶位置, 但仍然可见时,,这时Activity
就处于暂停状态; - 停止状态(Stopped state):驻留状态,当一个
Activity
不位于栈顶位置, 并完全不可见的时候, 这时Activity
就进入了停止状态;此时 Activity 不再和 window manager 关联了。 - 销毁状态(Destroyed state):驻留状态,当一个
Activity
从返回栈中移除后就变成了销毁状态。
1.3 Activity 的生命周期方法有哪些?
常用的生命周期方法有 7 个,如下:
onCreate
:表示Activity
正在被创建,可以在这里完成初始化的操作;onRestart
:表示Activity
正在被重新启动;onStart
:表示Activity
正在启动;onResume
:表示Activity
已经可见了,并且出现在前台并开始活动;onPause
:表示Activity
正在停止;onStop
:表示Activity
即将停止;onDestroy
:表示Activity
即将被销毁。
1.4 正常情况下,Activity 的生命周期切换过程是怎样的?
- 针对一个特定的
Activity
,第一次启动,回调为:onCreate
->onStart
->onResume
; - 当用户打开新的
Activity
或者切换回桌面的时候,回调为:onPause
->onStop
,而当新Activity
采用了透明主题或者是Dialog
主题 时,则当前Activity
不会回调onStop
; - 当用户再次回到原
Activity
时,回调为:onRestart
->onStart
->onResume
; - 当用户按 back 键回退时,回调为:
onPause
->onStop
->onDestroy
。
1.5 onStart 和 onResume、onPause 和 onStop 的区别是什么?
onStart
和 onStop
是从 Activity
是否可见这个角度来回调的,而 onResume
和 onPause
是从 Activity
是否位于前台,是否有焦点这个角度来回调的。
1.6 假设当前 Activity 为 A,如果这时用户打开一个新的 Activity B,那么 B 的 onResume 和 A 的 onPause 哪个先执行呢?
从源码中可以得出结论,是旧 Activity
先 onPause
,然后新 Activity
再 onResume
。因此,不能在 onPause
中做重量级的操作,以免影响新 Activity
进入前台。
1.7 异常情况下,Activity 的生命周期过程是怎样的?
分为两种情况来说明:
- 资源相关的系统配置发生改变导致
Activity
被杀死并重新创建:
当系统配置发生变化(比如旋转屏幕)后,Activity
会被销毁,其onPause
、onStop
、onDestroy
均会被调用,同时由于Activity
是在异常情况下终止的,onSaveInstanceState
会被调用来保存当前Activity
的状态。当Activity
被重新创建后,系统会调用onRestoreInstanceState
用来恢复之前保存的状态。 - 资源内存不足导致低优先级的 Activity 被杀死:
Activity
的优先级分为三种:前台Activity
,可见但非前台Activity
,后台Activity
。当系统内存不足时,系统会按照上述优先级去杀死目标 Activity 所在的进程,并在后续通过onSaveInstanceState
和onRestoreInstanceState
来保存和恢复数据。
1.8 onSaveInstanceState 什么情况下会被调用?
在 Activity 被异常终止时会被调用,在重建时回调 onRestoreInstanceState
;
屏幕方向切换时,会调用 onSaveInstanceState
,在重建时回调 onRestoreInstanceState
;
在按 Home 键或者启动新 Activity
时会单独调用 onSaveInstanceState
;
在手机锁屏时,会单独调用 onSaveInstanceState
;
1.9 onSaveInstanceState 和 onRestoreInstanceState 在生命周期中的调用时序是什么?
onSaveInstanceState
是在 onStop
之前,但和 onPause
没有既定的时序关系;
onRestoreInstanceState
是在 onStart
之后。
1.10 onCreate 和 onRestoreInstanceState 恢复数据时的区别是什么?
onRestoreInstanceState
一旦被调用,其参数 Bundle savedInstanceState
一定是有值的,不用进行判空操作;但是 onCreate
在正常启动时,其参数 Bundle savedInstanceState
为 null
,需要进行判空操作。建议采用 onRestoreInstanceState
来恢复数据。onRestoreInstanceState
在 onStart
之后调用。
1.11 onCreate 和 onRestart 的区别是什么?
这两个方法都是在 Activity
成为可见前调用的,但是 onCreate
只是 Activity
第一次成为可见前调用的,而之后 Activity
再次成为可见前,就会调用 onRestart
方法了。
1.12 当 Activity 在异常情况下需要重新创建时,系统是如何保存和恢复 View 层次结构的?
1.13 如何使得系统配置改变后,Activity 不会被重新创建?
可以给 Activity
指定 android:configChanges
,在这里列出 Activity
将自行处理的配置变更。在运行时发生配置变更时,默认情况下会关闭 Activity
并将其重启,但使用该属性声明配置将阻止 Activity
重启。相反,Activity
会保持运行状态,并且系统会调用其 onConfigurationChanged()
方法,不会调用 onSaveInstanceState()
和 onRestoreInstanceState()
方法。
实际开发车机应用遇到过,切换大灯会改变车机日夜间模式,没有配置 uiMode
(界面模式发生变更 — 用户已将设备置于桌面或车载基座,或者夜间模式发生变更) 在 android:configChanges
导致应用重启的问题。
相关面试题
Q1:Activity 上有 Dialog 时按 Home 键时的生命周期
A:onPause
-> onSaveInstanceState
-> onStop
。需要注意的是,在 Activity
中弹出 Dialog
时,并不会回调 onPause
方法;按 Home 键会单独触发 onSaveInstanceState
方法。
Q2:两个 Activity 跳转时必然会执行的是哪几个方法?
A:分情况分析一下:
当 Activity A 中打开新的 Activity B 时,这时 A 会回调 onPause
、onSaveInstanceState
、onStop
,B 会回调 onCreate
、onStart
、onResume
;
当 Activity A 中打开透明主题的 Activity B 时,这时 A 会回调 onPause
,B 会回调 onCreate
、onStart
、onResume
;
当 Activity A 打开 Activity B 时,B 已经存在于栈中并且 B 的启动模式是 singleTask
,这时 A 会回调 onPause
、onStop
、onDestroy
,B 会回调onRestart
、onStart
、onNewIntent
、onResume
方法。
综上分析,A 必然执行的方法是 onPause
,B 是onStart
、`onResume``。
Q3:adb shell am kill packagename 这个命令什么时候起作用?
这个命令用来告诉设备去终止指定包名的进程,但仅当那个应用是在后台的时候。
2 Activity 的启动模式
2.1 Activity 的四种启动模式对比?
- standard (标准模式),这是系统的默认模式。每次启动一个
Activity
都会创建一个新的实例,不管这个实例是否已经存在。被创建的实例的生命周期符合典型情况下的Activity
的生命周期。一个任务栈中可以有多个实例,每个实例也可以属于不同的任务栈。在这种模式下,谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity所在的栈中。 - singleTop(栈顶复用模式)。在这种模式下,若新
Activity
已经位于任务栈的栈顶,那么此Activity
不会被重新创建,同时它的onNewIntent
方法会被回调,但是onCreate
、onStart
方法不会被系统调用(这是因为它并有发生改变)。若新Activity
的实例已存在但不是位于栈顶,那么新Activity
仍然会重新创建。 - singleTask(栈内复用模式),这是一种单实例模式。在这种模式下,只要
Activity
在一个栈中存在,那么多次启动此Activity
都不会重新创建实例,同时它的onNewIntent
方法会被回调。 - singleInstance(单实例模式),这是一种加强的 singleTask 模式。加强的是具有此种模式的
Activity
只能单独地位于一个任务栈中。
参考:https://developer.android.google.cn/guide/topics/manifest/activity-element#lmode
2.2 指定 Activity 启动模式的方法及其区别是什么?
启动模式允许你去定义如何将一个Activity
的实例和当前的任务进行关联,有两种方法,第一种是在 AndroidManifest 中通过 android:launchMode
属性来给 Activity
指定启动模式,第二种是通过在 Intent
中设置标志位来为 Activity
指定启动模式。也就是说,当 Activity A 启动了 Activity B,第一种方式是 Activity B 定义自己如何与当前的任务进行关联,第二种方式是 Activity A 在 Intent
中要求 Activity B 怎样与当前任务进行关联。
区别:首先,优先级上,第二种方式的优先级要高于第一种,当两种都存在时,以第二种为准;其次,上述两种方式在限定范围上不同,比如,第一种方式无法直接为 Activity 设置 FLAG_ACTIVITY_CLEAR_TOP
标志,而第二种方式无法为 Activity 指定 singleInstance
模式。
2.3 查看任务栈的命令是什么?
输出所有的信息:
adb shell dumpsys activity
只输出 Activity 的信息:
adb shell dumpsys activity activities
2.4 有 3 个 Activity:A、B 和 C。其中 A 的启动模式是 standard,taskAffinity 是默认的,B 和 C 的启动模式是 singleTask,taskAffinity 不是默认的。进行如下操作,在 A 中单击按钮启动 B, 在 B 中单击按钮启动 C,在 C 中单击按钮启动 A,最后在 A 中单击按钮启动 B,现在按 2 次 back 键,然后看到的是哪个 Activity?
答案是回到桌面。
2.5 Activity 的 Flags
下面是几个常用的标记位:
-
FLAG_ACTIVITY_NEW_TASK
设置了这个
flag
,新启动Activity
就会被放置到一个新的任务当中(与singleTask
有点类似,但不完全一样)。和singleTask
的区别是:当使用 ApplicationContext 去启动一个standard
的Activity
时会报错,android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
这时只能通过给
Intent
添加FLAG_ACTIVITY_NEW_TASK
的方式解决报错,通过给新Activity
添加android:launchMode="singleTask"
的方式是不能解决报错的。 -
FLAG_ACTIVITY_SINGLE_TOP
等同于为
Activity
指定“singleTop”
启动模式。 -
FLAG_ACTIVITY_CLEAR_TOP
设置了这个flag,如果要启动的
Activity
在当前任务中已经存在了,就不会再次创建这个Activity
的实例,而是会把这个Activity之上的所有Activity全部关闭掉。。比如说,一个任务当中有A、B、C、D四个Activity
,然后D调用了startActivity()
方法来启动B,并将 flag 指定成FLAG_ACTIVITY_CLEAR_TOP
,那么此时C和D就会被关闭掉,现在返回栈中就只剩下 A 和 B了。此时Activity B会接收到这个启动它的Intent,你可以决定是让Activity B调用
onNewIntent()
方法(不会创建新的实例),还是将 Activity B 销毁掉并重新创建实例。如果 Activity B 没有在manifest中指定任何启动模式(也就是"standard"模式),并且Intent
中也没有加入一个FLAG_ACTIVITY_SINGLE_TOP
的 flag,那么此时 Activity B 就会销毁掉,然后重新创建实例。而如果Activity B在 manifest 中指定了singleTop
或者singleTask
,或者是在Intent
中加入了一个FLAG_ACTIVITY_SINGLE_TOP
flag,那么就会调用 Activity B 的onNewIntent()
方法。 -
FLAG_ACTIVITY_CLEAR_TASK
在启动 Activity 之前,会清除和启动的 Activity关联的存在的任务栈。
在实际开发中,作为系统应用集成在车机里面,发现从入口打开应用,按 home 键把应用退到后台,再从入口打开应用,应用都会先销毁再重新创建。询问了集成方,发现启动我们的应用时添加了
FLAG_ACTIVITY_CLEAR_TASK
和FLAG_ACTIVITY_NEW_TASK
这两个 flag 导致的。
2.6 当调用startActivity()方法来启动一个Activity时,如果在Intent中加入了一个FLAG_ACTIVITY_NEW_TASK flag的话(或者该Activity在manifest文件中声明的启动模式是"singleTask"),一定会开始一个新的任务栈吗?
系统就会尝试为这个 Activity
单独创建一个任务栈。但是规则并不是只有这么简单,系统会去检测要启动的这个 Activity
的 affinity
和当前任务栈的 affinity
是否相同,如果相同的话就会把它放入到现有任务栈当中,如果不同则会去创建一个新的任务栈。而同一个程序中所有 Activity
的 affinity
默认都是相同的,这也是前面为什么说,同一个应用程序中即使声明成"singleTask"
,也不会为这个Activity
再去创建一个新的任务栈了。
2.7 android:taskAffinity
是什么?在什么情况下起作用?
android:taskAffinity
即任务相关性,相似性,默认情况下,应用中的所有 Activity 都具有同一相似性。您可以设置该属性,以不同方式将其分组,甚至可以在同一任务内放置不同应用中定义的 Activity
。如要指定 Activity
与任何任务均无相似性,请将其设置为空字符串。
android:taskAffinity
主要和 singleTask
启动模式或者 allowTaskReparenting
属性配对使用,在其他情况下没有作用。
2.8 有 3 个 Activity:A,B 和 C. A 和 C 的启动模式都是 standard,B 的启动模式是 singleInstance。现在 A 打开 B,B 打开 C,那么 C 是处于 A 的任务栈中还是 B 的任务栈中?点击 back 键的退出顺序是什么样子的?
C 是 处于 A 的任务栈中。
第一次点击 Back,C 销毁,A 到前台;
第二次点击 Back,A 销毁,B 到前台;
第三次点击 Back,B 销毁。
3 IntentFilter 的匹配规则
3.1 启动 Activity 的两种方式
显式调用和隐式调用。当两种方式都存在时,显式调用优先于隐式调用。显式调用需要明确地指定被启动对象的组件信息,包括包名和类名;隐式调用不需要明确指定组件信息,需要 Intent
能够匹配目标组件的 IntentFilter
中所设置的过滤信息。
3.2 action 的匹配规则
action
的匹配要求 Intent
中的 action
存在且必须和过滤规则中的其中一个 action
相同。注意,Intent
中如果没有指定 action
,那么匹配失败。
指定在 Intent 中的 action 必须和过滤器中的 action 列表中的一个相匹配;
如果过滤器中不包含任何 action,所有的隐式 Intent 都会失败;
如果 Intent 中不指定 action,只要过滤器中包含有 action,那么就会通过测试。
3.3 category 的匹配规则
category
要求 Intent
可以没有 category
,但是如果你一旦有 category
,不管有几个,每个都要能够和过滤规则中的任何一个 category
相同。
指定在 Intent 中的 category 集合必须是过滤器中定义的 category 集合的子集。
3.4 data 的匹配规则
data
要求 Intent 中可以没有 data
数据,但是,如果你一旦有 data
,那么 data
数据能够完全匹配过滤规则中的某一个 data
。data
有两部分组成,mimeType
和 URI
。
3.5 为什么隐式调用时,必须在 intent-filter 中指定 “android.intent.category.DEFAULT” 这个 catogery?
这是因为系统在调用 startActivity
或者 startActivityForResult
的时候会默认为 intent加上 “android.intent.category.DEFAULT”
这个 catogery
。如果不指定,就会匹配失败。
3.6 如何实现指定分享功能?比如,手机上可以分享到微信,qq,wps 等,现在要求如果有微信应用可以分享,直接分享到微信;否则,采用选择器分享。
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("text/plain");
//分享内容标题
intent.putExtra(Intent.EXTRA_SUBJECT, "分享标题");
//分享内容
intent.putExtra(Intent.EXTRA_TEXT, "分享内容");
// 可以选择默认分享
if (getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
startActivity(intent);
}
// 不可以选择默认分享
if (getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
Intent chooser = Intent.createChooser(intent, "分享");
startActivity(chooser);
}
// 指定分享到微信
List<ResolveInfo> list = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
boolean matched = false;
for (int i = 0; i < list.size(); i++) {
ResolveInfo resolveInfo = list.get(i);
if ("com.tencent.mm.ui.tools.ShareImgUI".equals(resolveInfo.activityInfo.name)) {
matched = true;
intent.setComponent(new ComponentName("com.tencent.mm", "com.tencent.mm.ui.tools.ShareImgUI"));
startActivity(intent);
break;
}
}
if (!matched) {
startActivity(intent);
}
3.7 为什么通过隐式启动 Activity 时,使用 PackageManager 的 resolveActivity 或者 queryIntentActivities 方法时,第二个参数要传递 PackageManager.MATCH_DEFAULT_ONLY?
使用这个标志位是为了仅仅匹配那些在 intent-filter 中声明了 <category android:name="android.intent.category.DEFAULT" />
这个 category 的 Activity。如果不使用这个标志位,就会把 intent-filter 中 category 不含 DEFAULT 的那些 Activity 给匹配出来,从而导致 startActivity 可能失败。因为不含 DEFAULT 这个 category 的 Activity 是无法接收隐式 Intent 的。
3.8 如何获取应用注册的所有 Activity 和 Receiver?
try {
PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_ACTIVITIES);
ActivityInfo[] activities = packageInfo.activities;
if (activities != null) {
for (ActivityInfo activity : activities) {
Log.d(TAG, "onClick: " + activity.name);
}
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
try {
PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_RECEIVERS);
ActivityInfo[] activities = packageInfo.activities;
if (activities != null) {
for (ActivityInfo activity : activities) {
Log.d(TAG, "onClick: " + activity.name);
}
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
参考
1.《Android开发艺术探索》
2. Android任务和返回栈完全解析,细数那些你所不知道的细节
3. https://developer.android.google.cn/guide/components/intents-filters