Android官方文档和其他不少资料都对Activity生命周期进行了详细介绍,在结合资料和项目开发过程中遇到的问题,本文将对Activity生命周期进行一次总结。
Activity是由Activity栈进管理,当来到一个新的Activity后,此Activity将被加入到Activity栈顶,之前的Activity位于此Activity底部。Acitivity一般意义上有四种状态:
1.当Activity位于栈顶时,此时正好处于屏幕最前方,此时处于运行状态;
2.当Activity失去了焦点但仍然对用于可见(如栈顶的Activity是透明的或者栈顶Activity并不是铺满整个手机屏幕),此时处于暂停状态;
3.当Activity被其他Activity完全遮挡,此时此Activity对用户不可见,此时处于停止状态;
4.当Activity由于人为或系统原因(如低内存等)被销毁,此时处于销毁状态;
在每个不同的状态阶段,Adnroid系统对Activity内相应的方法进行了回调。因此,我们在程序中写Activity时,一般都是继承Activity类并重写相应的回调方法。
先贴一张来自官方文档(http://developer.android.com/reference/android/app/Activity.html)的图,相信大家都看到过。
图中详细给出了Activity整个生命周期的过程,以及在不同的状态期间相应的回调方法。
图中需要注意一下几点:
1.Activity实例是由系统自动创建,并在不同的状态期间回调相应的方法。一个最简单的完整的Activity生命周期会按照如下顺序回调:onCreate -> onStart -> onResume -> onPause -> onStop -> onDestroy。称之为entire lifetime。
2.当执行onStart回调方法时,Activity开始被用户所见(也就是说,onCreate时用户是看不到此Activity的,那用户看到的是哪个?当然是此Activity之前的那个Activity),一直到onStop之前,此阶段Activity都是被用户可见,称之为visible lifetime。
3.当执行到onResume回调方法时,Activity可以响应用户交互,一直到onPause方法之前,此阶段Activity称之为foreground lifetime。
在实际应用场景中,假设A Activity位于栈顶,此时用户操作,从A Activity跳转到B Activity。那么对AB来说,具体会回调哪些生命周期中的方法呢?回调方法的具体回调顺序又是怎么样的呢?
开始时,A被实例化,执行的回调有A:onCreate -> A:onStart -> A:onResume。
当用户点击A中按钮来到B时,假设B全部遮挡住了A,将依次执行A:onPause -> B:onCreate -> B:onStart -> B:onResume -> A:onStop。
此时如果点击Back键,将依次执行B:onPause -> A:onRestart -> A:onStart -> A:onResume -> B:onStop -> B:onDestroy。
至此,Activity栈中只有A。在Android中,有两个按键在影响Activity生命周期这块需要格外区分下,即Back键和Home键。我们先直接看下实验结果:
此时如果按下Back键,系统返回到桌面,并依次执行A:onPause -> A:onStop -> A:onDestroy。
此时如果按下Home键(非长按),系统返回到桌面,并依次执行A:onPause -> A:onStop。由此可见,Back键和Home键主要区别在于是否会执行onDestroy。
此时如果长按Home键,不同手机可能弹出不同内容,Activity生命周期未发生变化(由小米2s测的,不知道其他手机是否会对Activity生命周期有影响)。
由于Android本身的特性,使得现在不少应用都没有直接退出应用程序的功能,按照一般的逻辑,当Activity栈中有且只有一个Activity时,当按下Back键此Activity会执行onDestroy,那么下次点击此应用程图标将从重新启动,因此,当前不少应用程序都是采取如Home键的效果,当点击了Back键,系统返回到桌面,然后点击应用程序图标,直接回到之前的Activity界面,这种效果是怎么实现的呢?
通过重写按下Back键的回调函数,转成Home键的效果即可。
@Override
public void onBackPressed() {
Intent home = new Intent(Intent.ACTION_MAIN);
home.addCategory(Intent.CATEGORY_HOME);
startActivity(home);
}
当然,此种方式通过Home键效果强行影响到Back键对Activity生命周期的影响。注意,此方法只是针对按Back键需要退回到桌面时的Activity且达到Home效果才重写。
或者,为达到此类效果,Activity实际上提供了直接的方法。
1 activity.moveTaskToBack(true);
moveTaskToBack()此方法直接将当前Activity所在的Task移到后台,同时保留activity顺序和状态。
在之前的项目开发过程中,当时遇到一个很奇怪的问题:手机上的“开发者选项”中有一个“不保留活动”的设置,当开启此设置,手机上的设置提示是“用户离开后即销毁每个活动”,开启后,对于其他的应用程序是从A Acticity到B Activity,然后Back键回到A,此时,其他应用程序只是先白屏(有可能黑屏等,取决于主题设置)一下,然后A开始可见,但是我的应用程序中出现的一个结果却是直接返回到了桌面。一开始百思不得其解。最后终于定位出问题。首先,我们需要明确开启此设置项后对Activity生命周期的影响。开启此设置项后,当A到B时,假设B全部遮挡住了A,将依次执行A:onPause -> B:onCreate -> B:onStart -> B:onResume -> A:onStop -> A:onDestroy。是的,A在系统原本的生命周期回调中增加了onDestroy。此即“用户离开后即销毁每个活动”的含义。但此时需要注意的是,只要没有认为的调用A的finish()方法,虽然A执行了onDestroy,但Activity栈中依然保留有A,此时B处于栈顶。那么在B中按Back键回到A时,将依次执行:B:onPause -> A:onCreate -> A:onStart -> A:onResume -> B:onStop -> B:onDestroy。没错,A从onCreate开始执行了。此处也就解释了为什么A可能会出现白屏(或黑屏等)一下的原因了。
那么为什么我的应用程序会跟其他应用程序出现不一样呢?最后定为出问题在于当时我的应用程序中为了做到完全退出应用程序效果,专门使用了一个Activity栈去维护Activity(当时是借鉴了网上的此类实现方案,现在想想,实在没必要,且不说Android本身特性决定了没必要通过如此方法去达到退出效果,仅仅是此方法本身也存在很大的问题,现在在网上依然能见到有不少文章说到应用程序退出可以使用此方法,哎。。),在onCreate中入栈,onDestroy出栈,调用了如下方法:
1 // 结束Activity&从堆栈中移除
2 AppManager.getAppManager().finishActivity(this);
其中,AppManager中finishActivity函数具体定义是:
1 /**
2 * 结束指定的Activity
3 */
4 public void finishActivity(Activity activity) {
5 if (activity != null) {
6 activityStack.remove(activity);
7 activity.finish();
8 activity = null;
9 }
10 }
至此,相信大家应该看出问题的所在了吧。
没错,问题在于执行了activity的finish()方法!! activity的finish()方法至少有两个层面含义,1.将此Activity从Activity栈中移除,2.调用了此Activity的onDestroy方法。对于不开启“不保留活动”的设置项,实际上也没什么影响,但是一旦开启此设置,问题显露无疑。开启此此设置后,正常情况下离开A,即使执行了A的onDestroy,Activity栈中还是有A的,但是我这样写后,finish()方法一执行,Activity栈中就没有A了,因此,当点击Back键时,Activity栈中已经没有此应用的任何Activity了,直接来到了手机桌面。
可能,有些人会说,我就是要通过此种方法想去完全退出应用程序,同时希望自己的Activity栈和系统中Activity栈保持一致,怎么办呢?
在此,可以通过如下改写去实现:
/**
* 结束指定的Activity
*/
public void finishActivity(Activity activity) {
if (activity != null) {
// 为与系统Activity栈保持一致,且考虑到手机设置项里的"不保留活动"选项引起的Activity生命周期调用onDestroy()方法所带来的问题,此处需要作出如下修正
if(activity.isFinishing()){
activityStack.remove(activity);
//activity.finish();
activity = null;
}
}
}
以此谨记!
-------------------------------------------------
此外,对于不同的启动模式或Intent Flags或操作行为(如横竖屏切换)等有可能会影响到Activity生命周期,此类问题将放在后续相关文章中进行总结。
电源关(息屏)
此时Activity 会执行 onPause –> onStop
重新点开电源键(亮屏)
直接进入App(非分屏模式),Activity 会依次执行 onRestart —>onStart —> onResume
点击Back键
按下Back键,系统返回到桌面,并依次执行 onPause -> onStop -> onDestroy 。页面销毁;
点击app启动图标 ,会依次执行 onCreate —>onStart —> onResume
点击home键
按下Home键(非长按),系统返回到桌面,并依次执行 onPause -> onStop。
点击app启动图标 ,一种情况 :依次执行 onRestart —>onStart —> onResume 。
一种情况: 会依次执行 onCreate —>onStart —> onResume
点击Recent键(进入分屏模式)
按下Recent键(进入分屏模式),会依次执行onPause->onStop->onDestroy->onCreate->onStart-->onResume-->onPause
息屏(电源键关)会依次执行onStop,亮屏(电源键开)会进入桌面,会执行onReStart--->onStart,点击app启动图标,onResume, 息屏(电源键关)会依次执行onPause--->onStop.
退出分屏模式
会依次执行onStop--->OnDestroy--->OnCreate--->onStart--->OnReume。
Activity点击Home键,Back()键所走的生命周期
今天做了一个Demo测试,主要测试OnSaveInstanceState()方法在什么情况下会走。
1.点击Home键
当在一个Activity中,点击Home键时,打印出的log,中Activity的生命周期依次走的是,如图所示:
结果发现在OnPause()和OnStop()方法中间走的OnSaveInstanceState();
当再次从手机中程序运行的栈中,点击这个Activity,恢复当前这个Activity时候,生命周期走的是,如图所示:
结果生命周期没有变化。
当在开发者选项中,勾选上“不保留活动(用户离开后即销毁每个活动)”后,再次点击Home键,走的生命周期是,如图所示:
结果发现比之前多了一个OnDestroy()。
当再次从活动栈的列表中点击刚才的这个Actiivty时候,走的声明周期是,如图所示:
结果发现,如果选择上“不保留活动”的话,每一个Activity都会销毁,然后再次启动的时候都会重新走OnCreate()方法。
2.点击back键
1)没有勾选“不保留活动”
点击右下角的back键,走的生命周期是,如图所示:
2)勾选上“不保留活动”
点击右下角的back键,走的生命周期是,如图所示:
结果生命周期没有变化,唯一不同的是,点击了back键,不会再走OnSaveInstanceState(),对于这种主动要求退出的行为,app不会走OnSaveInstanceState()这个方法。
由此得出的结论是:
Activity的 onSaveInstanceState() 和 onRestoreInstanceState()并不是生命周期方法,它们不同于 onCreate()、onPause()等生命周期方法,它们并不一定会被触发。当应用遇到意外情况(如:内存不足、用户直接按Home键)由系统销毁一个Activity时,onSaveInstanceState() 会被调用。但是当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState()就不会被调用。因为在这种情况下,用户的行为决定了不需要保存Activity的状态。通常onSaveInstanceState()只适合用于保存一些临时性的状态,而onPause()适合用于数据的持久化保存。
Activity生命周期全面总结
前言
Android的生命周期相对来说是比较基础的知识点,但却是非常重要的。精准的掌握并熟练地使用会让我们的代码更有逻辑性且更加健壮。我们都知道人的记忆是曲线的,只有不断地回顾,不断地温习,这样在我们拿出来用的时候不至于出现间断性的记忆空白。
正文
我们今天来系统的总结一下Android的生命周期,比如正常状况下的生命周期,以及非正常状态下的生命周期。比如旋转屏幕时的生命周期以及异常情况下如何保存Activity的状态。先让我们来看一下官方给出的图解:
呀!有点看不懂。没关系,我刚开始也没看懂,让我们看不同操作生命周期的流程,写几个小Demo,相信我们回过头来再看就清晰多了。让我们来新建一个工程ActivityTest来验证一下每个流程。
public class MainActivity extends AppCompatActivity {
private static final String TAG = "ActivityTest";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "---onCreate()---");
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "---onStart()---");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG, "---onRestart()---");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "---onResume()---");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "---onPause()---");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "---onStop()---");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "---onDestroy()---");
}
}
我们先看一下七个方法的作用,然后再来分析一下不同情况的流程:
- onCreate() 表示Activity正在被创建
- onRestart() Activity正在重新启动
- onStart() activity变为在屏幕上对用户可见时调用
- onResume() 开始与用户交互时调用
- onPause() Activity正在停止,失去焦点
- onStop() Activity即将停止,且处于不可见状态
- onDestory() Activity正在被销毁
正常情况
第一次启动一个Activity
依次调用:onCreate-->onStart-->onResum 让我们来看一下Log。
当我们点击Back退回到桌面
依次调用:onPause-->onStop-->onDestory 让我们来看一下Log。
当我们点击Home键返回桌面时
依次调用:onPause()-->onStop() 让我们来看一下Log。
注意当我们点击Home键退回到桌面时并没有调用onDestory,因为此时Activity只是处于后台不可见的状态,并没有被销毁。
当我们从后台切回到Activity时
依次调用onRestart-->onStart-->onResume 我们看一下log
我们会发现并没有调用onCreate,因为此前Activity处于停滞状态,并没有被销毁所以不需要重新创建。但是值得注意的是当系统资源不足时会导致后台Activity被杀死。当Activity被杀死的时候,你去启动它你就会发现和第一次启动它的生命周期是相同的。接下来我们看看非正常情况下的生命周期。
onWindowFocusChanged
在我们介绍它非正常情况的生命周期之前,我们先来看看它的onWindowFocusChanged()方法。这个onWindowFocusChanged指的是这个Activity得到或者失去焦点的时候会被调用,也许这么说不太好理解,我们可以认为他是Activity完全加载之后调用。
启动Activity
退出Activity
如果之前你写过FrameAnimation你会发现,当我们在onCreate内start动画时,Activity启动了但并没有动画的效果,如果你将start放在onWindowFocusChanged内就会达到预期的效果了。这是因为在onCreate的时候Activity并没有获得焦点,而onWindowFocusChanged是在Activity获得焦点之后调用。
非正常情况
屏幕旋转
当屏幕旋转的时候我们发现Activity先是被销毁之后又被重建。而且我们还发现虽然Activity被重建了,但是控件里的内容还在,这是为什么呢。
这是因为当Activity不是正常退出的时候,它不仅调用onPause,onStop,onDestory,它会在调用onStop之前调用onSaveInstanceState这个方法来保存一些数据,当Activity重建的时候,这个方法所存储的数据会以Bundle作为参数传递给onCreate,所以我们可以用参数来判断这个Activity是新建的还是重建的,如果是重建的,则可以从Bundle中取出数据来恢复界面。
那我们可不可以不让他销毁并重新创建呢?
答案是肯定的,你只需要在AndroidManifest文件中的对应Activity中配置
android:configChanges="keyboardHidden|orientation|screenSize",最好这三个都配置,否则不能适配所有机型或sdk版本。
当然如果你不想让他旋转就在AndroidManifest文件中的对应Activity中配置android:screenOrientation=”landscape”(横屏,portrait是竖屏);