《Android开发艺术探索》第1章-Activity 的生命周期和启动模式读书笔记

本文详细解析了Android中Activity的生命周期,包括典型与异常情况下的状态转换、生命周期方法、启动模式及IntentFilter匹配规则,帮助开发者掌握Activity管理的关键。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

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 的区别是什么?

onStartonStop 是从 Activity 是否可见这个角度来回调的,而 onResumeonPause 是从 Activity 是否位于前台,是否有焦点这个角度来回调的。

1.6 假设当前 Activity 为 A,如果这时用户打开一个新的 Activity B,那么 B 的 onResume 和 A 的 onPause 哪个先执行呢?

从源码中可以得出结论,是旧 ActivityonPause,然后新 ActivityonResume。因此,不能在 onPause 中做重量级的操作,以免影响新 Activity 进入前台。

1.7 异常情况下,Activity 的生命周期过程是怎样的?

分为两种情况来说明:

  • 资源相关的系统配置发生改变导致 Activity 被杀死并重新创建:
    当系统配置发生变化(比如旋转屏幕)后, Activity 会被销毁,其 onPauseonStoponDestroy 均会被调用,同时由于 Activity 是在异常情况下终止的, onSaveInstanceState 会被调用来保存当前 Activity 的状态。当 Activity 被重新创建后,系统会调用 onRestoreInstanceState 用来恢复之前保存的状态。
  • 资源内存不足导致低优先级的 Activity 被杀死:
    Activity 的优先级分为三种:前台 Activity,可见但非前台 Activity,后台 Activity。当系统内存不足时,系统会按照上述优先级去杀死目标 Activity 所在的进程,并在后续通过 onSaveInstanceStateonRestoreInstanceState 来保存和恢复数据。

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 savedInstanceStatenull,需要进行判空操作。建议采用 onRestoreInstanceState 来恢复数据。onRestoreInstanceStateonStart 之后调用。

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 会回调 onPauseonSaveInstanceStateonStop,B 会回调 onCreateonStartonResume
当 Activity A 中打开透明主题的 Activity B 时,这时 A 会回调 onPause,B 会回调 onCreateonStartonResume
当 Activity A 打开 Activity B 时,B 已经存在于栈中并且 B 的启动模式是 singleTask,这时 A 会回调 onPauseonStoponDestroy,B 会回调onRestartonStartonNewIntentonResume方法。
综上分析,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 方法会被回调,但是 onCreateonStart 方法不会被系统调用(这是因为它并有发生改变)。若新 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 去启动一个 standardActivity 时会报错,

    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_TASKFLAG_ACTIVITY_NEW_TASK 这两个 flag 导致的。

2.6 当调用startActivity()方法来启动一个Activity时,如果在Intent中加入了一个FLAG_ACTIVITY_NEW_TASK flag的话(或者该Activity在manifest文件中声明的启动模式是"singleTask"),一定会开始一个新的任务栈吗?

系统就会尝试为这个 Activity 单独创建一个任务栈。但是规则并不是只有这么简单,系统会去检测要启动的这个 Activityaffinity 和当前任务栈的 affinity 是否相同,如果相同的话就会把它放入到现有任务栈当中,如果不同则会去创建一个新的任务栈。而同一个程序中所有 Activityaffinity 默认都是相同的,这也是前面为什么说,同一个应用程序中即使声明成"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 数据能够完全匹配过滤规则中的某一个 datadata 有两部分组成,mimeTypeURI

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

willwaywang6

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值