Android基础知识汇总

Android基础知识汇总

简述在横竖屏切换的过程中,Activity的生命周期。

onPause() ---> onSaveInstanceState() -- > onStop() --> onDestory() --> onCreate() --> onRestoreInstanceState() --> onResume()

在 Activity 因异常情况(例如:系统配置变更(如屏幕旋转)、系统内存不足)而终止时,系统会调用 onSaveInstanceState() 来保存 Activity 的当前状态。当用户主动按返回键销毁 Activity 时,该方法不会被调用。onSaveInstanceState() 的调用时机在 onStop() 之前,但两者之间没有固定的先后顺序。该方法仅在 Activity 因异常情况被终止时才会触发。当异常终止的 Activity 被重建后,系统会调用 onRestoreInstanceState()onCreate() 方法。因此,可以通过 onRestoreInstanceState() 方法来恢复 Activity 的状态。该方法的调用时机在 onStart() 之后。

可以通过在AndroidManifest文件的Activity中指定如下属性来避免横竖屏切换:

android:configChanges = "orientation| screenSize"

Activity的Flags

Activity 的 Flags 有很多种,这里介绍几种常用的,它们主要用于设定 Activity 的启动模式。在启动 Activity 时,可以通过 Intent.addFlags() 方法进行设置。

  • FLAG_ACTIVITY_NEW_TASK :对应 singleTask 启动模式。
  • FLAG_ACTIVITY_SINGLE_TOP :对应 singleTop 启动模式。
  • FLAG_ACTIVITY_CLEAR_TOP:当它启动时,在同一个任务栈中,所有位于它之上的 Activity 都会被出栈。
  • 如果与 singleTask 模式一起出现,且被启动的 Activity 已经存在于栈中,则会清除其之上的所有 Activity,并调用该 Activity 的 onNewIntent() 方法。
  • 如果被启动的 Activity 采用 standard 模式,那么该 Activity 连同其上的所有 Activity 都会出栈,然后系统会创建新的 Activity 实例并压入栈中。
Activity的启动过程
  • 当在 Launcher 上点击应用图标后,Launcher 会通过 Binder 跨进程调用机制通知 AMS(ActivityManagerService),告知其要启动一个 Activity。
  • AMS 再通过 Binder 进程间通信机制通知 Launcher 进入 Paused 状态。
  • Launcher 通过 Binder 跨进程调用机制通知 AMS,它已准备就绪并进入 Paused 状态。随后,AMS 会创建一个新的进程,用于启动一个 ActivityThread 实例,即将要启动的 Activity 就在这个 ActivityThread 实例中运行。
  • ActivityThread 实例通过 Binder 将一个 ApplicationThread 类型的 Binder 对象传递给 AMS,以便 AMS 能够通过这个 Binder 与它进行通信。
  • AMS 通过 Binder 进程间通信机制通知 ActivityThread,现在一切准备就绪,可以真正执行 Activity 的启动操作了。
Activity启动模式

Android 提供了四种 Activity 启动模式:

1. 标准模式 standard

每启动一次 Activity,就会创建一个新的 Activity 实例并置于任务栈栈顶。该 Activity 会运行在启动它的那个 Activity 所在的任务栈中。

特殊情况下,如果在 Service 或 Application 中启动一个 Activity,由于它们本身没有任务栈,可以使用标记位 Flag 来解决。具体做法是:为待启动的 Activity 指定 FLAG_ACTIVITY_NEW_TASK 标记位,系统会为其创建一个新的任务栈。

2. 栈顶复用模式:singleTop

如果需要启动的 Activity 已经位于任务栈栈顶,那么系统不会重建该 Activity 的实例,而是直接复用栈顶的实例,并回调其 onNewIntent() 方法:

java 复制代码
@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
}

由于不会重建 Activity 实例,因此不会回调 onCreate() 等其他生命周期方法。

应用场景 :例如在通知栏点击通知启动一个 Activity。使用 singleTop 可以避免每次点击都新建一个 Activity 实例。

3. 栈内复用模式:singleTask

该模式是一种单例模式,即在一个任务栈中只允许存在一个该 Activity 的实例。singleTask 具有 clearTop 特性,启动时会清除位于它之上的所有 Activity。与 singleTop 一样,系统也会回调它的 onNewIntent() 方法。

当一个 singleTask 模式的 Activity 请求启动时,系统会首先检查是否存在它所需的任务栈:

  • 如果不存在,则创建一个新的任务栈,然后创建该 Activity 的实例并放入栈中。
  • 如果存在所需的任务栈,则进一步检查该 Activity 是否已在栈中存在实例:
    • 如果存在,系统会将该实例调到栈顶,并调用其 onNewIntent() 方法。
    • 如果不存在,则创建新的实例并压入栈顶。

该模式允许在 AndroidManifest.xml 文件中通过 taskAffinity 属性指定 Activity 希望加载到哪个任务栈中。singleTasktaskAffinity 配合使用,可以精确控制 Activity 所属的栈。

kotlin 复制代码
<activity android:name=".Activity1"
    android:launchMode="singleTask"
    android:taskAffinity="com.test.task"
    android:label="@string/app_name">
</activity>

关于 taskAffinitytaskAffinity 可以理解为任务相关性,它标识了 Activity 所期望的任务栈名称。每个 Activity 都有 taskAffinity 属性,该属性指明了它希望进入的任务栈。如果 Activity 没有显式指定 taskAffinity,则其值等于 Application 指定的 taskAffinity;如果 Application 也未指定,则默认等于包名。

以上述配置为例,Activity1 指定了 taskAffinity="com.test.task"。在这种情况下启动 Activity1

  • 如果 "com.test.task" 栈不存在,则创建该栈,并将 Activity1 实例压入栈中。
  • 如果 "com.test.task" 栈存在,但其中没有 Activity1 的实例,则创建 Activity1 实例并压入栈顶。
  • 如果 "com.test.task" 栈中已存在 Activity1 的实例,则系统会执行 clearTop 操作,清除该实例之上的所有 Activity,使该实例回到栈顶,然后调用其 onNewIntent() 方法。
singleTask 应用场景

对于大多数应用,主界面(如首页)通常被设置为 singleTask 模式。当用户首次进入主界面后,主界面位于栈底。之后无论打开多少其他 Activity,只要用户再次回到主界面(例如点击返回键),系统都会通过清除主界面上方所有 Activity 的方式,让主界面回到栈顶,而不是在栈顶新建一个主界面实例。这样可以确保退出应用时,所有 Activity 都能被正常销毁。

4. 单例模式:singleInstance

作为栈内复用模式的加强版,当打开一个 singleInstance 模式的 Activity 时,系统会直接为其创建一个新的任务栈,并将该 Activity 实例放入此栈中。一旦该模式的 Activity 实例存在于某个栈中,任何应用再激活该 Activity 时都会直接重用这个栈中的实例。

应用场景:例如来电接听界面。这种模式确保了该 Activity 独占一个任务栈,避免与其他 Activity 混在一起。

场景分析


假设当前有两个任务栈:前台任务栈中有 A、B 两个 Activity(自栈底到栈顶,下同),后台任务栈中有 C、D 两个 Activity。如果 C 和 D 都被设置为 singleTask 模式,此时通过 A 启动 D,那么整个后台任务栈都会被切换到前台。可以理解为任务栈的顺序变为 A、B、C、D。此时按返回键后,D 出栈,C 显示;再次按返回键,C 出栈,B 显示,以此类推。

如果上述场景中 A 启动的是 C 而不是 D,那么 D 会被首先 clearTop 清除掉,即任务栈变为 A、B、C。此时按返回键,C 先出栈,接着 B、A 依次出栈。

思考


1. 一个被设置为 standard 模式的 Activity 在被 ApplicationContext 启动时为什么会报错?

因为 standard 模式的 Activity 默认会进入启动它的 Activity 所属的任务栈。然而,非 Activity 类型的 Context(如 ApplicationContext)自身并没有所属的任务栈,因此此时启动 standard 模式的 Activity 就会产生问题。可以通过为待启动的 Activity 指定 FLAG_ACTIVITY_NEW_TASK 标记位来解决,这样在启动该 Activity 时系统会为其创建一个新的任务栈。此时,这个 Activity 实际上是以 singleTask 模式启动的。

2. 有 A、B、C 三个 Activity,其中 A 启动模式为 standard,taskAffinity 为默认。B 和 C 的启动模式都设置为 singleTask,并且都指定 taskAffinity 为 "com.test.task"。此时,如果在 A 中点击按钮启动 B,在 B 中点击按钮又启动 C,在 C 中点击按钮又启动 A,最后在 A 中点击按钮启动 B。现在按两次返回键会回到哪里?

会回到桌面。具体流程如下:

  1. A 启动 B:由于 B 指定了独立的任务栈 "com.test.task",因此系统会首先创建该任务栈(以下简称 T 栈),然后实例化 B 并将其加入 T 栈。
  2. B 启动 C:由于 C 与 B 指定的都是 T 栈,因此 C 会实例化后加入到 T 栈。
  3. C 启动 A:此时 T 栈中没有 A,因此会实例化 A 并将其加入 T 栈。
  4. A 再次启动 B:由于 B 已经存在于 T 栈中,因此会执行 clearTop 操作,将 C 和 A 都出栈,然后显示出 B。
  5. 此时按第一次返回键:B 被 finish 掉,此时后台任务栈中的 A 会显示出来。
  6. 按第二次返回键:A 结束,回到桌面。
相关推荐
布鲁飞丝1 小时前
从零实现富文本编辑器#-浏览器选区与编辑器选区模型同步
java·前端·编辑器
small-pudding1 小时前
Cocos Creator Android 热更新方案实践:逻辑更新、远程 Bundle 与线上回滚
android
jun_bai2 小时前
使用java安全的移动文件
java
00后程序员张2 小时前
iOS加固技术路线全面解析:Bitcode模式、源码模式与汇编模式对比及爱加密优势
android·汇编·ios·小程序·uni-app·cocoa·iphone
caishenzhibiao2 小时前
市场同步系统 同花顺期货通指标
java·c语言·c#
Devin~Y2 小时前
从本地生活电商到 AI RAG:互联网大厂 Java 面试场景完整实战
java·spring boot·redis·elasticsearch·spring cloud·kafka·rag
520拼好饭被践踏2 小时前
JAVA+Agent学习day25
java·开发语言·学习
大黄说说2 小时前
Java 并发大坑:volatile、synchronized、Lock 三者如何选择?
java·开发语言
summerkissyou19872 小时前
android - 性能 - Perfetto - 内存分析教程及例子
android·性能