前言
接触过Android开发的同学都知道Activity,Activity作为Android四大组件之一,使用频率高。简单来说Activity提供了一个显示界面,让用户进行各种操作,本文主要分为以下三个部分:Activity的生命周期,启动模式,以及Activity的工作过程。文中大部分篇幅来自《Android开发艺术探索》一书,尽管想多以流程或图片的形式说明,奈何文笔有限,Android系统整体把握还不够深入,只能借鉴参考前辈的成果,本文主要目的是帮自己梳理Activity的相关问题,疏漏之处欢迎指出。
说明:本文中的源码都是基于Android-25版本。
Activity生命周期
生命周期
首先给出Activity的完整生命周期,如下图所示:
图片来源Android开发者官网
看完了完整的生命周期,我们再分别简单介绍单个生命周期含义。
onCreate
:表示Activity正在被创建,做一些初始化工作,如加载布局文件,对一些变量或者控件进行初始化onStart
:表示Activity正在启动,此时Activity已经可见了,但还无法和用户交互onResume
:表示Activity已经在前台,可以和用户交互了onPause
:表示Activity正在停止,此时可以进行一些轻量级的存储数据和停止动画等工作,但不能太耗时,否则会影响到新Activity的显示,Android指定如果onPause
在500ms内没有执行完毕的话就会强制关闭Activity。某些极端情况下,这个时候Activity会回到当前Activity,此时onResume
会被执行,很难重现这一情形onStop
:表示Activity即将停止,此时Activity已经不可见了,但是Activity对象还存在内存中,没有被销毁,这个阶段也主要做一些资源回收的工作,不能太耗时onDestory
:表示Activity即将被销毁,可以做一些回收工作和最终的资源释放,最后一个回调方法onRestart
:表示Activity重新启动,当前Activity从不可见重新变为可见时会调用onRestart
,这种情况一般是用户的行为导致的,比如:用户按Home键切换到桌面或者打开了一个新的Activity
三个问题
onCreate,onStart和onResume之间的区别?
onCreate
时Activity还不可见,onStart
已经可见,还不在前台,onResume
已经出现在前台onCreate
方法只在Activity创建时执行一次,而onStart
方法在Activity的切换以及按Home键返回桌面再切回应用的过程中被多次调用,因此数据恢复在onStart
方法中比较合适,onResume
方法可以做一些开启动画和独占设备的操作
onPause,onStop和onDestroy之间的区别?
onPause
时Activity还可见,onStop
已经不可见,但Activity对象还存在内存中,onDestroy
后Activity对象就不存在了- 内存不足时,可能不会执行
onStop
方法,因此程序状态保存,停止动画,存储数据等操作最好放在onPause
中
切换Activity时生命周期执行顺序
假设当前Activity为A,此时用户打开Activity B,那么A和B的生命周期的执行顺序为:
为了验证生命周期执行顺序,我们写个例子实测,MainActivity
中单击按钮跳转到SecondActivity
,然后在相应的生命周期中输出日志,如下:
|
|
日志结果如下:
|
|
那么为什么会有这样的生命周期执行顺序呢?Android官方文档对onPause的解释为:不能在onPause中做重量级的操作,因为必须onPause执行完成以后新Activity才能Resume
,同时我在分析Activity启动源过程中码也得到了相同结论。
如果SecondActivity是完全透明的或者是对话框,则最后不会调用MainActivity的onStop()
Activity保存状态
当 Activity 暂停或停止时,Activity 的状态会得到保留。因为当 Activity 暂停或停止时,Activity 对象仍保留在内存中 ,有关其成员和当前状态的所有信息仍处于活动状态。
但是当系统的配置发生变化(屏幕旋转,语言变换等)时,Activity 被销毁,系统回调 onSaveInstanceState()
方法对有关 Activity 状态的信息进行保存,系统会向该方法传递一个 Bundle
,可以在其中使用 putString()
和 putInt()
等方法以名称-值对
形式保存有关 Activity 状态的信息。当 Activity 重建时将 Bundle
同时传递给 onCreate()
和 onRestoreInstanceState()
方法,使用上述任一方法从 Bundle
提取保存的数据并恢复该 Activity 状态。
图片来源Android开发者官网
在 onSaveInstanceState()
和 onRestoreInstanceState()
方法中,系统自动为我们做了一定的恢复工作,布局中的每个 View 调用自己的 onSaveInstanceState()
方法,让每个视图都能保存自身需保存的信息。
每个 View 都有 onSaveInstanceState()
和 onRestoreInstanceState()
方法,具体能恢复哪些数据,需要查看其源码。保存和恢复 View 用到了事件委托思想,从下至上,再从上到下。需要注意的是,只有我们为 View 指定了唯一的ID属性(android:id
)系统才会自动为其在需要时自动保存和恢复数据。
通过重写onSaveInstanceState()
和 onRestoreInstanceState()
实现自定义的数据恢复机制。
那么 onSaveInstanceState()
和 onRestoreInstanceState()
方法和 Activity 的生命周期的执行顺序是怎么样的?onSaveInstanceState()
方法是在onStop
之前,和onPause
没有既定的时序关系, onRestoreInstanceState()
方法是在onStart
之后、onResume
之前调用的。
如何避免配置改变时Activity重建?
- 设置配置文件中Activity的configChanges属性
- 参考回答:为了避免由于配置改变导致Activity重建,可在AndroidManifest.xml中对应的Activity中设置android:configChanges=”orientation|screenSize”。此时再次旋转屏幕时,该Activity不会被系统杀死和重建,只会调用onConfigurationChanged。因此,当配置程序需要响应配置改变,指定configChanges属性,重写onConfigurationChanged方法即可
Activity启动模式
默认情况下,当我们多次启动同一个Activity时,系统会创建多个实例并把它们一一放入任务栈中,任务栈是一种后进先出的栈结构,每按下back键就会有一个Activity出栈,直到栈空为止。
standard:标准模式
这是系统默认的模式,每次启动一个新的Activity都会重新创建一个新的实例,不管这个实例已经是否存在。这种模式下,如果A启动了B(标准模式),那么B自动进入A所在的任务栈中。
singleTop:栈顶复用模式 (登录页面、推送通知栏)
此种模式下,如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,同时onNewIntent方法会被调用,通过此方法的参数获取当前请求信息。而且,此Activity的onCreate,onStart不会被调用,因为没有发生改变。
- singleTop模式分3种情况:
- 当前栈中已有该Activity的实例并且该实例位于栈顶时,不会新建实例,而是复用栈顶的实例,并且会将Intent对象传入,回调onNewIntent方法
- 当前栈中已有该Activity的实例但是该实例不在栈顶时,其行为和standard启动模式一样,依然会创建一个新的实例
- 当前栈中不存在该Activity的实例时,其行为同standard启动模式
standard和singleTop启动模式都是在原任务栈中新建Activity实例,不会启动新的Task,即使你指定了taskAffinity属性
singleTask:栈内复用模式(应用中展示的主页(Home页))
此种模式下,只要Activity在一个栈中存在,那么多次启动此Activity都不会重新创建实例,系统会回调onNewIntent方法。系统会先寻找是否存在A想要的任务栈,如果不存在,就重建一个任务栈,然后创建A的实例放入新任务栈中;如果存在A想要的任务栈,再查看是否有Activity实例存在,有的话就把该实例调到栈顶,如果实例不存在,则创建A的实例放入任务栈中。下面举三种例子说明此种模式运行机制:
- 目前任务栈S1中为ABC,Activity D以
singleTask
模式请求启动,其需要的任务栈为S1,那么系统会创建D的实例,将D放入S1中 - 目前任务栈S1中为ABC,Activity D以
singleTask
模式请求启动,其需要的任务栈为S2,那么系统会创建任务栈S2,再将D放入S2中 - 目前任务栈S1中为ADBC,Activity D以
singleTask
模式请求启动,其需要的任务栈为S1,那么系统不会创建D的实例,将D切换到栈顶并调用其onNewIntent
方法,同时栈内所有在D上面的Activity都需要出栈,最终的S1为AD
singleInstance:单实例模式 (系统Launcher、锁屏键、来电显示等系统应用)
这是一种加强的singleTask
模式,除了具有singleTask模式的所有特性外,具有此种模式的Activity只能单独位于一个任务栈中。比如Activity A以singleInstance
模式启动,系统会为其创建一个新的任务栈,然后A独自运行在该任务栈中,后续的请求均不会创建新的Activity。
以singleInstance模式启动的Activity具有独占性,即它会独自占用一个任务,被他开启的任何activity都会运行在其他任务中(官方文档上的描述为,singleInstance模式的Activity不允许其他Activity和它共存在一个任务中)
任务栈
在启动模式中多次提到Activity的任务栈,默认情况下,所有Activity所需任务栈的名字为应用的包名。此外还有一个参数TaskAffinity
,这个参数标识了Activity所需要的任务栈的名字,TaskAffinity
属性主要和singleTask
模式或者allowTaskReparenting
属性配对使用,具体请参考《Android开发艺术探索》P19
和Android任务和返回栈完全解析,细数那些你所不知道的细节。
如何指定Activity启动模式
- 第一种是通过AndroidManifest.xml 指定:
|
|
- 第二种是通过在Intent中设置标志位,比如:1234Intent intent = new Intent();intent.setClass(MainActivity.this, SecondActivity.class);intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);startActivity(intent);
这两种方式都可以为Activity指定启动模式,但是二者还是有区别的:
- 优先级上第二种高于第一种,两种同时存在,以第二种为准
- 第一种无法为Activity设定FLAG_ACTIVITY_CLEAR_TOP标识
- 第二种无法为Activity指定singleInstance模式
Activity启动过程源码分析
聊完了Activity的生命周期及启动模式,最头疼的部分来了,那就是一个Activity究竟是如何启动的呢?以下篇幅结合源码一探究竟,拨开云雾见青天。在显式调用启动Activity时,我们通过以下代码完成:
上述代码便可以启动一个Activity,下面我们便从源头理清Activity的启动流程,当然我们不可能对所有细节描述清楚,需要做的是理清流程,点到为止。
startActivity/startActivityForResult
我们从Activity的startActivity
方法开始分析,读源码可知,startActivity
方法有好几种重载形式,但最终都调用了startActivityForResult
方法,因此我们重点分析startActivityForResult
方法:
|
|
Instrumentation
我们主要关注mParent == null
这一部分逻辑,首先,mParent
是一个Activity对象,表示该Activity是否由父Activity启动,如果该Activity是第一个被启动的,那么就会调用Instrumentation
的execStartActivity
方法:
|
|
Instrumentation
是管理Activity的一个工具类,包括创建和启动Activity,Activity的生命周期方法都是由Instrumentation
这个类来控制,一个进程中只用一个Instrumentation
实例。execStartActivity
是真正启动Activity的操作,下面解释其参数:
- who : 待启动的Activity对象
- mMainThread.getApplicationThread() : ApplicationThread对象,也是Binder对象
- mToken :Binder对象,指向服务端一个ActivityRecord对象
- target :待启动的Activity对象
- intent :启动的intent对象
- requestCode :请求码
- options :参数
AMS
再回到execStartActivity
方法体中,其中可以看到真正实现Activity启动的是ActivityManagerNative.getDefault().startActivity
方法。下面列出了ActivityManagerNative.getDefault()
的源码:
|
|
其中声明了一个Singleton
封装类,类型是IActivityManager
,第一次调用它的get方法它会通过create
方法来初始化AMS
这个Binder
对象。其中在asInterface
方法中返回了一个IActivityManager
类型对象。ActivityManagerService(AMS)
继承自ActivityManagerNative
,而ActivityManagerNative
继承自Binder
并实现了IActivityManager
接口,因此AMS
是一个Binder
,同时也是IActivityManager
的具体实现,ActivityManagerNative.getDefault()
其实是一个IActivityManager
类型的Binder
对象,即AMS
。我们理一下上面提到的各种类和接口之间的关系:
|
|
所以Activity的启动过程转移到AMS
中,下面分析AMS
的startActivity
方法:
ActivityStarter
上述代码中mActivityStarter
是一个ActivityStarter
对象,初始化为:
可以看出Activity的启动过程转移到ActivityStarter
的startActivityMayWait
方法中,startActivityMayWait
又调用了startActivityLocked
方法:
|
|
startActivityLocked
方法又调用startActivityUnchecked
方法:
|
|
startActivityUnchecked
调用了ActivityStack
的startActivityLocked
方法和ActivityStackSupervisor
的resumeFocusedStackTopActivityLocked
方法:
|
|
ActivityStackSupervisor
ActivityStackSupervisor
的resumeFocusedStackTopActivityLocked
方法如下:
|
|
ActivityStack
下面进入ActivityStack
的resumeTopActivityUncheckedLocked
方法:
|
|
紧接着又调用了resumeTopActivityInnerLocked
方法:
|
|
resumeTopActivityInnerLocked
方法又调用了ActivityStackSupervisor
的startSpecificActivityLocked
方法:
startSpecificActivityLocked
方法调用了realStartActivityLocked
方法:
|
|
上面这段代码中,其中app.thread
的类型是IApplicationThread
,IApplicationThread
的声明如下:
|
|
IApplicationThread
继承了IInterface
,所以它是一个Binder
类型的接口,IApplicationThread
内部包含了大量启动、停止Activity的接口,此外还包含启动和停止服务的接口。IApplicationThread
的实现者就是ActivityThread
的内部类ApplicationThread
,相关类的继承和实现关系如下所示:
说了这么多,分析下来不仅累,还容易被绕晕,画张流程图总结下:
ApplicationThread
绕了一圈,Activity的启动过程还是回到了ApplicationThread
中,ApplicationThread
通过scheduleLaunchActivity
方法启动Activity,源码如下:
|
|
其主要实现就是发送一个启动消息交由Handler处理,这个Handler有个特殊的名字叫H
(是不是很萌的名字),sendMessage
的实现如下:
|
|
H
对Activity启动的消息进行处理:
|
|
ActivityThread
从上面代码可以看出,Activity的启动过程又转到ActivityThread
的handleLaunchActivity
方法中,其源码如下:
上述代码中的performLaunchActivity
方法最终完成了Activity对象的创建和启动过程,performLaunchActivity
这个方法主要完成了以下几件事:
摘自Android开发艺术探索
- 从ActivityClientRecord中获取待启动的Activity的组件信息
|
|
- 通过Instrumentation的newActivity方法使用类加载器创建Activity对象
|
|
- 通过LoadedApk的makeApplication方法来创建Application对象
|
|
- 创建ContextImpl对象,并通过Activity的attach方法来完成一些重要数据的初始化
|
|
ContextImpl
是一个很重要的数据结构,很明显是Context
的具体实现,Context
中大部分逻辑都是由ContextImpl
实现的。ContextImpl
通过Activity的attach
方法来和Activity建立关联,同时在attach
方法中还会完成Window
的创建并建立自己和Window
的关联,Window
接收到外部事件输入后将事件传递给Activity。
- 调用Activity的onCreate方法
|
|
上述代码中调用Activity的onCreate
方法,意味着Activity已经完成了整个启动过程。此外还调用了Activity的onStart
,OnRestoreInstanceState
方法:
|
|
总结
至此,整个Activity的启动流程已经分析完了,其中还有很多细节需要打磨:
- 启动过程中涉及到的类的作用分别是什么,我们并没有深入分析
- 可以看出,Activity启动最后涉及到Handler消息循环机制,可以参考我的上一篇文章:Android消息机制Handler
- Activity启动过程中AMS的地位显著,但是进程间通信的具体本质我也没有分析,下一步可以分析下Android IPC机制
- Android从开机到点击Icon打开APP的过程中涉及到Launcher(它其实也是一个android应用程序)的启动以及APP进程的创建,我们此处没有提及,以后的博客中我也会整理分析
参考信息
- 任玉刚.《Android开发艺术探索》
- Android Launcher 启动 Activity 的工作过程