一、Activity启动流程
启动流程 image.png关键的过程:
- Activity1调用startActivity,实际会调用Instrumentation类的execStartActivity方法,Instrumentation是系统用来监控Activity运行的一个类,Activity的整个生命周期都有它的影子。
- Instrumentation有一个AMS的代理对象(IActivityManager、它来自于 ActivityManagerNative.getDefault())通过跨进程的binder调用,进入到ActivityManagerService中,其内部会处理Activity栈,通知Activity1 Pause,Activity1 执行Pause 后告知AMS。
- 在ActivityManagerService中的startProcessLocked中调用了Process.start()方法。并通过连接调用Zygote的native方法forkAndSpecialize,执行fork任务。之后再通过跨进程调用进入到Activity2所在的进程中。
- ApplicationThread是一个binder对象,其运行在binder线程池中,内部包含一个H类,该类继承于类Handler。主线程发起bind Application,AMS 会做一些配置工作,然后让主线程 bind ApplicationThread,ApplicationThread将启动Activity2的信息通过H对象发送给主线程。发送的消息是EXECUTE_TRANSACTION,消息体是一个 ClientTransaction,即 LaunchActivityItem。主线程拿到Activity2的信息后,调用Instrumentation类的newActivity方法,其内通过ClassLoader创建Activity2实例。
- 通知Activity2去performCreate。
两个hook点:
第一个hook点: Instrument类的AMS Proxy对象(IActivityManager),会传递Activity启动参数;我们目的是拦截startActivity,所以改变IActivityManager对象可以做到这个一点,这里gDefault又是静态的,根据Hook原则,这是一个比较好的Hook点。动态代理掉IActivityManager对象,就可以拿到startActivity的intent参数了,可以伪造自己的intent参数,启动真实activity。
第二个hook点:ActivityThread中的H对象,有一段这样的代码,先执行callback,再执行handleMessage。所以我们反射ActivityThread,hook H对象,然后让H执行自己的callback,先替换intent参数,然后再调用handleMessage执行原来执行流程。
二、Activity关键类:ActivityStackSupervisor、ActivityStack、TaskRecord、ActivityRecord
1、理解这几个类
1.1 一个系统就一个ActivityStackSupervisor
1.2 一个应用有几个ActivityStack
在 Android 系统版本 < 9.0 设备上,一个应用就一个ActivityStack;
在 Android 系统版本 >= 9.0 设备上,每新建一个 TaskRecord,都会同时新建一个 ActivityStack
1.3 一个ActivityStack有几个TaskRecord
有N个,ActivityStack保存了一个ArrayList的TaskRecord对象
final class ActivityStack {
private ArrayList<TaskRecord> mTaskHistory = new ArrayList<>();
}
1.4 一个TaskRecord有几个ActivityRecord
有N个,TaskRecord保存了一个ArrayList的ActivityRecord对象
final class TaskRecord {
/** List of all activities in the task arranged in history order */
final ArrayList<ActivityRecord> mActivities;
}
TaskRecord表示任务栈,是真正的一个先进后出的栈结构,用于记录activity开启的先后顺序。
1.5 ActivityRecord保存了什么信息
Activity实例与ActivityRecord实例一一对应。ActivityRecord用来存储Activity的信息,如所在的进程名称,应用的包名,所在的任务栈的taskAffinity等。
final class ActivityRecord {
final ComponentName realActivity; // the intent component, or target of an alias.
final String shortComponentName; // the short component name of the intent
final String packageName; // the package implementing intent's component
final String processName; // process where this component wants to run
final String taskAffinity; // as per ActivityInfo.taskAffinity
boolean fullscreen; // covers the full screen?
}
三、Activity的启动模式
1、standard - 标准模式:
创建 Activity 的实例,并 添加到启动它的源 Activity 所在的任务的栈顶。不管栈内或栈顶是否已存在该 Activity 的实例。
2、singleTop - 栈顶复用模式:
当 启动它的源 Activity 所在的任务的栈顶已存在 该 Activity 的实例,那么不创建该 Activity 的新实例 —— 而是走该 Activity 实例的 onNewIntent 回调,注入新的 intent,并执行 onResume(也就是不走 onCreate、onStart)。
否则就在栈顶创建一个新实例。
3、singleTask - 栈内复用模式:
当 该 Activity 所属的任务中已存在 该 Activity 的实例,那么不创建该 Activity 的新实例 —— 而是 首先将任务中该 Activity 实例之上的 Activity 全都出栈,并且走该 Activity 实例的 onNewIntent 回调,注入新的 intent,并执行 onResume。
否则就在栈顶创建一个新实例。
(singleTask Activity 的所属任务,取决于清单中配置的 taskAffinity,如果没有用 taskAffinity 指定任务名,默认是 Activity 所属 App 的默认任务。)
4、singleInstance - 单例模式:
会新建一个任务,并且 独享这个任务。也即 整个系统 有且只有 这么一个 Activity 的实例,多个 App 可共享该实例。
5、taskAffinity(亲和度)
“亲和性”表示 Activity 倾向于属于哪个任务。默认情况下,同一应用中的所有 Activity 彼此具有亲和性。因此,在默认情况下,同一应用中的所有 Activity 都倾向于位于同一任务。不过,您可以修改 Activity 的默认亲和性。在不同应用中定义的 Activity 可以具有相同的亲和性,或者在同一应用中定义的 Activity 也可以被指定不同的任务亲和性。
亲和性可在两种情况下发挥作用:
5.1 当启动 Activity 的 intent 包含 FLAG_ACTIVITY_NEW_TASK 标记时。
默认情况下,新 Activity 会启动到调用 startActivity()的 Activity 的任务中。它会被推送到调用方 Activity 所在的返回堆栈中。但是,如果传递给 startActivity() 的 intent 包含 FLAG_ACTIVITY_NEW_TASK标记,则系统会寻找其他任务来容纳新 Activity。通常会是一个新任务,但也可能不是。如果已存在与新 Activity 具有相同亲和性的现有任务,则会将 Activity 启动到该任务中。如果不存在,则会启动一个新任务。
如果此标记导致 Activity 启动一个新任务,而用户按下主屏幕按钮离开该任务,则必须为用户提供某种方式来返回到该任务。有些实体(例如通知管理器)总是在外部任务中启动 Activity,而不在它们自己的任务中启动,因此它们总是将 FLAG_ACTIVITY_NEW_TASK 添加到传递给 startActivity() 的 intent 中。
5.2 当 Activity 的 allowTaskReparenting属性设为 "true" 时。
在这种情况下,一旦和 Activity 有亲和性的任务进入前台运行,Activity 就可从其启动的任务转移到该任务。
举例来说,假设一款旅行应用中定义了一个报告特定城市天气状况的 Activity。该 Activity 与同一应用中的其他 Activity 具有相同的亲和性(默认应用亲和性),并通过此属性支持重新归属。当您的某个 Activity 启动该天气预报 Activity 时,该天气预报 Activity 最初会和您的 Activity 同属于一个任务。不过,当旅行应用的任务进入前台运行时,该天气预报 Activity 就会被重新分配给该任务并显示在其中。
6、Activity清除操作
6.1 默认策略
如果用户将任务切到后台,过了很长一段时间,系统会将这个任务中除了最底层的那个 Activity 之外的其它所有 Activity 全部清除。
当用户重新回到这个任务时,最底层的那个 Activity 将得到恢复。
6.2 alwaysRetainTaskState
如果在清单文件中,将最底层的那个 Activity 的这个属性设置为 true,那么上面所描述的默认行为就将不会发生,任务中所有的Activity即使过了很长一段时间之后仍然会被继续保留。
6.3 clearTaskOnLaunch
如果将最底层的那个 Activity 的这个属性设置为 true,那么只要用户离开了当前任务,再次返回的时候就会将最底层 Activity 之上的所有其它Activity全部清除掉。简单来讲,就是一种和 alwaysRetainTaskState 完全相反的工作模式,它保证每次返回任务的时候都会是一种初始化状态,即使用户仅仅离开了很短的一段时间。
6.4 finishOnTaskLaunch
这个属性和 clearTaskOnLaunch 是比较类似的,不过它不是作用于整个任务上的,而是作用于单个 Activity 上。如果某个 Activity 将这个属性设置成 true,那么用户一旦离开了当前任务,再次返回时这个 Activity 就会被清除掉。
四、生命周期
1、Activity生命周期
image.png
2、Activity跳转和回退的生命周期回调
image.png
image.png
3、系统回收进程
image.png
建议用LifeCycle组件来感知Activity生命周期变化
LifeCycle定义了一套生命周期的状态机,并根据注册来回调
五、Activity页面状态保存恢复(旋转屏幕、多窗口、语言变化等引起的页面创建)
image.png1、什么时候系统会保存Activity状态,什么时候不会
1.1 旋转屏幕、多窗口模式的切换、语言变化,系统回收进程,都会保存Activity状态
1.2 主动调用finish之后,系统不会保存Activity的状态;正常返回键处理逻辑(不重写onBackPressed),不会保存Activity状态
2、onSaveInstanceState
2.1 使用 onSaveInstanceState() 保存简单轻量的界面状态
在引发重建时,Activity 会自动为我们保存和恢复 View 的状态。具体表现为:
Activity 在 Save 时,会 自动收集 View Hierachy 中每一个 “实现了状态保存和恢复方法” 的 View 的状态,这些状态数据会在 Restore 时回传给每一个 View。并且回传时是依据 View 的 id 来逐一匹配的。
1.为了成功保存状态,要求在 View 内部实现状态保存和恢复方法。
原生的 View 都有做到;如果是自定义 View,务必记住这一点;如果第三方 View 没做到,可以通过继承其来实现保存和恢复方法。
2.为了成功恢复状态,要求在布局中给 View 赋予相应的 id。
3.如果是 Activity 的成员变量,需要额外在 Activity(子类)中重写 Save 和 Restore 方法。
onSaveInstanceState只适合轻量级数据的保存(最好在50KB以内),Binder通信有一个限制是1MB以内,所以在onSaveInstanceState的bundle中最大也不能超过1MB。
onPause不能处理耗时操作,onStop可以用于保存一些数据
建议用ViewModel和LiveData组合来保存Activity状态和发送事件进行通信
页面成员的状态保存和恢复,可以通过 Jetpack ViewModel 来简化,
从前你需要在 定义常量、序列化实体对象、在 onSaveInstanceState 和 onRestoreInstanceState 中各写一遍状态保存恢复的代码(至少包含以上 4 个步骤,步骤越多,容易疏忽和出错的地方就越多,严重拖累和阻碍业务功能的编写)
现在你只需 “将成员托管给 Jetpack ViewModel” 这一步就可以享受 “可暂存和恢复的成员”。
Jetpack ViewModel 仅仅是使 “状态保存和恢复” 更简便和不易出错,但如果某些场景临时数据的恢复十分重要,那么可以考虑持久化存储,此处的策略是改为 “间歇性提前自动将临时数据从内存 RAM 中备份到硬盘 ROM 中”。
六、不同级别的进程
1、前台进程(获得焦点的进程)
处于onResume状态的Activity、正在执行代码的service(onCreate|onStart|onDestory)和broadcast(onReceive)
2、可见进程(可见但没有焦点的进程)
- 它正在运行的 Activity在屏幕上对用户可见,但不在前台(其 onPause()方法已被调用)。举例来说,如果前台 Activity 显示为一个对话框,而这个对话框允许在其后面看到上一个 Activity,则可能会出现这种情况。
- 它有一个 Service正在通过 Service.startForeground()(要求系统将该服务视为用户知晓或基本上对用户可见的服务)作为前台服务运行。
- 系统正在使用其托管的服务实现用户知晓的特定功能,例如动态壁纸、输入法服务等。
3、服务进程(后台服务的进程)
虽然用户无法直接看到这些进程,但它们通常正在执行用户关心的任务(例如后台网络数据上传或下载),因此系统会始终使此类进程保持运行,除非没有足够的内存来保留所有前台和可见进程。
已经运行了很长时间(例如 30 分钟或更长时间)的服务的重要性可能会降位,以使其进程降至下文所述的缓存 LRU 列表。这有助于避免超长时间运行的服务因内存泄露或其他问题占用大量内存,进而妨碍系统有效利用缓存进程
4、缓存进程(可回收的进程)
这些进程通常包含用户当前不可见的一个或多个 Activity实例onStop() 方法已被调用并返回。只要它们正确实现其 Activity 生命周期,那么当系统终止此类流程时,就不会影响用户返回该应用时的体验,因为当关联的 Activity 在新的进程中重新创建时,它可以恢复之前保存的状态。
网友评论