总结activity生命周期
参考:
- Android官方培训课程中文版(v0.9.7)
- Android:图解四种启动模式 及 实际应用场景解说
- Android-Activity四种启动模式
- activity生命周期(这篇足够了)
- 《第一行代码》
- 解决PendingIntent参数传递问题
- Intent.addFlags() 启动Activity的20种flags全解析
- ActivityLifecycleCallbacks
文章目录
1. activity生命周期
activity在它的生命周期里面最多可能会有4种状态:运行状态、暂停状态、停止状态、销毁状态。最核心的回调函数有6个:onCreate()
、onStart()
、onResume()
、onPause()
、onStop()
、onDestroy()
。
图片来自安卓开发国内镜像
(1) 运行状态
运行状态,也就是用户可见并且可以跟activity进行交互的状态。
涉及到的三个比较核心的回调函数onCreate()
、onStart()
、onResume()
。
当activity创建的时候,先执行onCreate()
,此时activity处于created状态,但是这个状态很短暂。马上就会执行onStart()
,进入started状态,同样,这个状态也非常短暂。马上就会执行onResume
,进入resumed状态,并且如果没有别的事情发生的话,activity将一直处于这个状态。
-
onCreate()
activity创建或者首次启动的时候会调用的方法。在这个方法里面可以进行一些基本的初始化操作,例如定义UI、实例化类成员变量。但是只能做基本的初始化工作,不要做太耗时间的初始化工作,不然界面会半天加载不出来,要是超时严重的话还有可能停止运行。如果要读一大批数据,不要直接在onCreate()里面做,可以考虑开线程和添加handler,避免把主界面卡死。setContentView()
onCreate() 里面调用setContentView() 就是为了绑定当前activity的View,否则在初始化界面的时候就不能直接使用findViewById()
,而是要写完整的view.findViewById()
。 -
onStart()
activity由不可见变为可见的时候调用。 -
onResume()
系统调用完onStart()
之后会迅速调用onResume()
,使得activity停留在Resumed状态(如果没有其他因素干扰的话)。这时activity已经准备好跟用户进行交互。
(2) 暂停状态
暂停状态,activity处于 部分可见(半透明) 的状态,但是用户不能跟activity交互。例如,刚好从activityA切换到activityB时,在切换的那一瞬间activityA会进入onPause()
。又比如,刚好有一个对话框(Dialog或者DialogFragment)从activityA弹出来,这时用户可以看到activityA,但是由于它的表面有一个对话框,所以用户没办法直接对activityA进行操作,除非把对话框关掉。
暂停状态涉及到一个回调函数onPause()
。
-
onPause()
系统准备去启动或者恢复另一个activity时调用,意味着用户准备离开当前这个activity,并且马上要进入Stopped状态。通常,应该在
onPause()
里面做下面的事情:(1)停止动画或者其他正在运行的操作,因为这些都会导致CPU资源浪费。 (2)提交用户离开时希望保存的数据,例如邮件草稿。 (3)释放系统资源,例如BroadcastReceiver、照相机,或者是任何会影响到电量的资源。
需要注意的地方是:
虽然(2)提到可以保存数据,但是必须明确的是在onPause()
里面保存的数据只是针对那些必须及时保存的数据,例如邮件草稿。不应该把全部保存数据的操作放到onPause()
里面,尤其是写入数据库或者本地文件这种要对数据进行永久存储的操作。我们应该避免在onPause()
里面进行高强度消耗CPU的操作,例如前面说的写入数据库或文件,原因跟在onCreate()
里面读大量数据差不多,会卡界面。因为onPause()
就是切换activity,如果在切换过程中执行高强度消耗CPU的操作,当前这个要暂停的activity可能会等上一段时间才能暂停,而那个准备要切换的activity又切换不出来,结果就是界面加载很久都看不到。
(3) 停止状态
停止状态跟暂停状态最大的不同是,停止状态是UI完全不可见的,并且用户的焦点转移到了另一个activity上。
停止状态涉及到的回调函数是onStop()
。
- onStop()
当activity调用onStop()
时,activity不再可见,并且应该释放所有不再需要的资源(除了一部分线程以外)。不应该在onPause()
做高强度消耗CPU的操作,应该在onStop()
里面进行。一旦activity停止,系统会在需要内存空间的时候销毁它的实例(和栈结构有关)。极端情况下,系统会直接杀死整个APP进程,而且不执行onDestroy()
。这也是为什么要在onStop()
释放资源,否则极有可能出现内存泄漏。
恢复和重启
恢复activity就是从onPause()
回到onResume()
。
重启activity就是从onStop()
到onRestart()
再到onStart()
最后到onResume()
。
onPause()
对应onResume()
,onStop()
对应onStop()
。
系统每次调用
onPause()
时,activity都处于前台,包括第一次创建的时候。所以,应该实现onResume()
来初始化那些在onPause()
里面释放掉的组件,并执行那些activity每次进入Resumed状态都需要的初始化动作(例如开始动画与初始化那些只有在获取用户焦点时才需要的组件)。
当activity从Stopped状态回到前台时,它会调用
onRestart()
,然后再调用onStart()
。当activity从Stopped状态恢复时,必须执行一些特殊的恢复工作来重新实例化那些被清楚的资源。由于使用onRestart()
来做恢复状态不太常见,所以应该在onStart()
里面重新把那些清除掉的资源重新创建出来。
(4) 销毁状态
销毁状态是activity的最后一个状态,意味着activity被彻底移除。
跟销毁状态直接相关的回调函数是onDestroy()
。
-
onDestroy()
事实上,onDestroy()
并不算是很常用的方法,至少跟前面的五个回调方法相比是这样的。当app从一个activity跳到另一个activity并不会执行onDestroy()
,而是执行onStop()
,除非主动调用了finish()
或者点击了导航栏上的返回键(系统调用onBackPressed()
)又或者屏幕旋转了(系统先执行onDestroy()
再onCreate()
),又或者是当前app占用过多内存被系统强行杀死才会进入调用onDestroy()
。但这并不意味着
onDestroy()
不需要实现。如果当前activity开启了线程或者当前app开启了service的话,就要在onDestroy()
的时候关掉,不然会内存泄漏。
重新创建
当系统因为某个app占用过多资源而强行执行onDestroy()
的时候,系统会把activity的记录保存下来,这些被系统用来恢复之前状态而保存下来的数据叫做instance state,它的保存形式是放在Bundle
对象中的键-值对。
- onSaveInstanceState()
当用户离开activity的时候,系统会调用这个方法。当系统调用这个函数时,系统会在activity被异常destroy时传递Bundle对象,这样我们就可以增加额外的信息到Bundle并保存到系统中,用于activity的重建。
通常来说,跳转到其他的activity或者是点击HOME都会导致当前的activity执行
onSaveInstanceState()
,因为这种情况下的activity都是有可能会被destroy并且是需要保存状态以便后续恢复使用的。而从跳转的activity点击BACK回到前一个activity时,因为跳转前的activity执行的是退栈操作(不可能存在需要重建的操作),所以这种情况下系统不会执行onSaveInstanceState
。
返回栈
android是使用任务(task)来管理activity的,一个任务就是一组存放在栈里的activity的集合,这个栈也被称作返回栈(backstack)。在默认情况下,每当启动了一个新的activity,它就会在返回栈中入栈,处于栈顶。如果用户按HOME键或者BACK键,处于栈顶的activity会出栈(被销毁),而前一个入栈的activity就会处在栈顶。那些有机会恢复状态的activity都是没有出栈的,只是暂时不处于栈顶而已,过后还是有可能处在栈顶的,所以系统才会执行onSaveInstanceState()
。
正常出栈的activity根本没必要保存现场,因为它的移除是永久的,如果要跑这个activity,就是重新入栈。对于异常出栈的activity,系统会自动保存它的状态。
- onRestoreInstanceState()
若系统在activity被destroy之后想要重新创建这个activity实例时,之前的Bundle对象会被传递到activity的onRestoreInstanceState()
方法和onCreate()
方法中。onRestoreInstanceState()
会在onStart()
方法之后执行,可以用来恢复数据。系统仅仅会在存在需要回复的状态信息时下回调用onRestoreInstanceState()
,因此不需要像在onCreate()
里面那样检查Bundle是否为null
。
ActivityLifecycleCallbacks
安卓官方提供了一个ActivityLifecycleCallbacks的接口,可以通过这个接口获取当前app里面所有的activity的生命周期。实现这个接口里面的一部分方法,并且把app注册到这个接口上就可以使用了。
2. 启动模式
activity有四种启动模式:standard、singleTop、singleTask、singleInstance。
(1) standard
标准模式是默认的启动模式,每次启动一个activity就会创建一个新的activity实例,不管栈里面有没有这个实例。
每次启动activity时都是创建一个新的实例,所以涉及的回调函数就是:onCreate()
、onStart()
、onResume()
、onPause()
、onStop()
、onDestroy()
。
(2) singleTask
栈内复用模式,也可以叫栈内单例模式。栈内只有该activity的实例只有一个。如果栈内已经存在这个activity,而这个activity不在栈顶,这时要启动这个activity,系统会直接把栈内这个activity上面的别的实例出栈,直接把这个activity变成栈顶。
例如现在栈内的情况是:A(栈底)、B、C、D、E(栈顶)。
现在要启动C,假设C的启动模式是singleTask,那么系统就会把D和E直接出栈,此时栈内情况变成:A(栈底)、B、C(栈顶)。
由于每一次启动activity时会检查栈内有没有这个activity的实例,所以如果没有进行栈内复用的时候,就相当于走standard模式,系统会调用6个回调函数。如果进行了栈内复用的话,系统调用的回调函数是:onNewIntent()
、onResume()
、onPause()
、onStop()
、onDestroy()
,系统不会执行onCreate()
和onStart()
。
(3) singleTop
栈顶复用模式。如果要创建的activity的实例已经处在栈顶,那么系统就不会创建这个activity的实例,会直接使用当前处于栈顶的这个实例。如果要创建的activity的实例没有处在栈顶,那么系统就会像标准模式一样创建这个activity的实例。
由于每一次启动activity时会检查栈顶,如果栈顶不能复用的话就相当于走了一次standard模式,所以还是涉及那6个回调函数。但是如果栈顶复用了的话,涉及到的回调函数就会变成onNewIntent()
、onResume()
、onPause()
、onStop()
、onDestroy()
,系统不会执行onCreate()
和onStart()
。
(4) singleInstance
全局单例模式,也叫堆内单例模式。整个操作系统里面只有一个实例存在。
onNewIntent(Intent intent)丢失Extras
主要是因为传进来的那个Intent
走完super.onNewIntent()
之后可能会莫名其妙地丢了,而且事实上每分每秒都有很多不知道从哪里来的Intent
进入这个onNewIntent()
的函数里,所以才说不能直接getIntent()
或者直接拿参数列表里面那个intent
。网上普遍推荐的方法是在super.onNewIntent()
之后再使用setIntent()
来更新在这个方法里面的Intent
。
如果是使用PendingIntent
来启动intent
的话,就不需要setIntent()
了。可以直接把PendingIntent
的最后一个参数flag
设置为PendingIntent.FLAG_UPDATE_CURRENT
,就是把当前这个PendingIntent
的数据更新为当前最新的数据。因为系统里面也是有无数个PendingIntent
,然而系统觉得它们是一个东西,所以如果flag
参数设置为0的话,那么这个PendingIntent
所携带的extras
百分之一百会丢失。
3. 应用场景
Android-Activity四种启动模式第二点的表格。
Android:图解四种启动模式 及 实际应用场景解说第二点。
4. activity的flag
Intent.addFlags() 启动Activity的20种flags全解析