Android学习笔记:Android中活动的生命周期(正常和异常)解析
2018-02-04 18:31
483 查看
对活动的生命周期有了充分的了解才能对程序作出最好的优化,给用户最好的使用体验。
Android官方给出了活动的生命周期图,不过是英文的,翻译后如下所示(作图真考验耐心):
Activity类中有7个回调方法,覆盖了活动生命周期的每一个环节。
1、onCreate()。活动第一次被创建时调用。
2、onStart()。活动由不可见变为可见时调用。
3、onResume()。在活动准备好和用户进行交互时调用,也被称为获得输入焦点时调用。
4、onPause()。在系统准备去启动或者恢复另一个活动的时候调用,也被称为失去输入焦点时调用。
5、onStop()。在活动完全不可见时调用,若A活动启动的B活动为对话框式活动时,A活动此时不是完全不可见,则onPause()会得到执行,onStop()不会被执行。
6、onDestory()。活动被销毁之前调用。
7、onRestart()。活动被重新启动时调用,只能在onStop()方法之后调用。停止态 -> 运行态之前。
体验活动生命周期:
准备,新建一个项目ActivityLifeCycleTest,包含一个活动(MainActivity),再新建两个子活动,一个是正常的覆盖屏幕的活动(NormalActivity),另一个是对话框式活动(DialogActivity),并让两个子活动布局拥有一个TextView,用于明示这是哪一个活动。在主活动添加两个按钮,分别用于启动两个活动,重写上述7个方法,分别打印Log日志,通过观察MainActivity的方法调用情况,来理解活动的生命周期。
将DialogActivity设置为对话框式活动可以在AndroidManifest文件中,在该活动的<activity>标签中加入:
修改activity_normal.xml文件。
修改activity_dialog.xml文件。
修改activity_main.xml文件。
修改MainActivity中的代码。
1、运行程序:
由于原图太大,所以截取了一部分,省去了空白。
Logcat图:
主活动从创建、开始、获得输入焦点。经历了三个过程。
2、启动NormalActivity,将主活动完全覆盖。
Logcat图:
启动了NormalActivity,主活动经历了暂停、停止两个过程。
3、按下返回键,返回主活动。
Logcat图:
返回主活动时,由于是从停止态到运行态的过程,所以onRestart()方法得到执行,活动重新启动,并经历开始、获得输入焦点的过程。
4、启动DialogActivity。此时主活动并不是完全不可见。
Logcat图:
由于主活动并不是完全不可见,所以onStop()方法不会得到执行,所以只会执行onPause()方法。
5、按下返回键,回到主活动。
Logcat图:
同样的,返回主活动时,由于不是从停止态向运行态过度,所以只执行onResume()方法。重新获得输入焦点。
6、在主活动上,按下返回键结束程序。
Logcat图:
从运行态到结束活动,会依次执行暂停、停止、销毁方法。
另外的,活动被回收了怎么办。例:活动A启动了活动B,活动A进入了停止态,但是由于系统内存不足将A活动回收了,此时按下返回键,从B活动回到A活动会经历什么?
同样的道理,A活动被回收,从停止态直接销毁。执行onDestory()方法。回到A活动的过程中,A活动会被重新创建,从onCreate()方法开始执行,而不会执行到onRestart()
方法,所以,理解活动生命周期很重要,一定要避免重要数据未保留,而活动却被系统回收。
了解了活动的生命周期后,还有一些重点需要掌握。即活动的异常的生命周期。
导致异常的情况有两种:
1、系统配置发生变化导致活动被杀死并重新创建(多指横竖屏切换)。
2、由于内存不足导致处于停止态的低优先级的活动被杀死。
首先,从第一种情况开始说明,我们可以利用上面讲到的程序进行分析,首先在重写的7个方法上面再重写两个方法。如下:
这次执行程序需要模拟器模拟横竖屏切换。我使用的是Genymotion模拟器,同时按下Ctrl + F11即可实现横竖屏切换。
运行程序,并将竖屏切换为横屏。注意Loacat输出。
运行图:
此时Logcat图:
按下Ctrl + F11,切换为横屏。
此时Logcat图:
可以看到,活动被杀死后重新创建。暂停后执行保存数据方法,然后执行停止、销毁方法,然后创建活动,执行创建、开始方法,并执行恢复数据方法,然后执行onResume()方法获得输入焦点。
我们的预期是将横竖屏切换不会影响到活动的生命周期,而是从什么状态切换,到什么状态结束。上面的例子就是异常的活动生命周期。
然而我们绝对不希望活动的运行是这样的,到底要怎么避免旋转屏幕后活动销毁重建呢?那就是为活动设置configChange属性。即configChange:"orientation",在SDK>13时还要增加一个screenSize属性,要怎么使用呢?还是以上面的程序为例,在AndroidManifest.xml文件的主活动定义标签内加入下面一句话:
这些还不够,光一条语句不能实现预期,它的意义就是:横竖屏切换时不会让活动销毁重建,而是回调onConfigurationChanged()方法。在MainActivity中重写该方法。
然后再次运行程序,Logcat中的输出如下(运行结果图及屏幕旋转过程略):
可以看到活动不会再销毁重建了,而是运行了onConfigurationChanged()方法。
第二种情况,系统由于内存不足而将处于停止态的低优先级活动杀死,这个其实就是活动生命周期被以外终止,依次执行暂停、保存数据、停止、销毁方法。我们要做的就是尽量保证活动的优先级高。
进程优先级分5种,优先级从高到低分别为:1、前台进程。2、可视进程。3、服务进程。4、后台进程。5、空进程。
尽量避免停止态中的活动不会被系统杀死的做法就是,尽量将活动所在的进程设置为前台进程,或者通过service绑定,也可以使其成为一个单独的进程。
本文总结参考自郭神(郭霖)的《第一行代码 第2版》以及zejian的活动解析博客。
Android官方给出了活动的生命周期图,不过是英文的,翻译后如下所示(作图真考验耐心):
Activity类中有7个回调方法,覆盖了活动生命周期的每一个环节。
1、onCreate()。活动第一次被创建时调用。
2、onStart()。活动由不可见变为可见时调用。
3、onResume()。在活动准备好和用户进行交互时调用,也被称为获得输入焦点时调用。
4、onPause()。在系统准备去启动或者恢复另一个活动的时候调用,也被称为失去输入焦点时调用。
5、onStop()。在活动完全不可见时调用,若A活动启动的B活动为对话框式活动时,A活动此时不是完全不可见,则onPause()会得到执行,onStop()不会被执行。
6、onDestory()。活动被销毁之前调用。
7、onRestart()。活动被重新启动时调用,只能在onStop()方法之后调用。停止态 -> 运行态之前。
体验活动生命周期:
准备,新建一个项目ActivityLifeCycleTest,包含一个活动(MainActivity),再新建两个子活动,一个是正常的覆盖屏幕的活动(NormalActivity),另一个是对话框式活动(DialogActivity),并让两个子活动布局拥有一个TextView,用于明示这是哪一个活动。在主活动添加两个按钮,分别用于启动两个活动,重写上述7个方法,分别打印Log日志,通过观察MainActivity的方法调用情况,来理解活动的生命周期。
将DialogActivity设置为对话框式活动可以在AndroidManifest文件中,在该活动的<activity>标签中加入:
<activity android:name=".DialogActivity" android:theme ="@style/Theme.AppCompat.Dialog" > </activity>
修改activity_normal.xml文件。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="This is NormalActivity " /> </LinearLayout>
修改activity_dialog.xml文件。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="This is DialogActivity " /> </LinearLayout>
修改activity_main.xml文件。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/start_normal_activity" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Start NormalActivity" /> <Button android:id="@+id/start_dialog_activity" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Start DialogActivity" /> </LinearLayout>
修改MainActivity中的代码。
package com.my.activitylifecycletest; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; public class MainActivity extends AppCompatActivity { //tag说明,用于log日志,简化书写 private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "onCreate"); //输出onCreate()方法执行语句 setContentView(R.layout.activity_main); Button startNormalActivity = (Button)findViewById(R.id.start_normal_activity); Button startDialogActivity = (Button)findViewById(R.id.start_dialog_activity); //注册监听事件,启动NormalActivity startNormalActivity.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this,NormalActivity.class); startActivity(intent); } }); //注册监听事件,启动NormalActivity startDialogActivity.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this,DialogActivity.class); startActivity(intent); } }); } //重写活动生命周期的7个方法 @Override protected void onStart() { super.onStart(); Log.d(TAG, "onStart"); } @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"); } @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "onRestart"); } }
1、运行程序:
由于原图太大,所以截取了一部分,省去了空白。
Logcat图:
主活动从创建、开始、获得输入焦点。经历了三个过程。
2、启动NormalActivity,将主活动完全覆盖。
Logcat图:
启动了NormalActivity,主活动经历了暂停、停止两个过程。
3、按下返回键,返回主活动。
Logcat图:
返回主活动时,由于是从停止态到运行态的过程,所以onRestart()方法得到执行,活动重新启动,并经历开始、获得输入焦点的过程。
4、启动DialogActivity。此时主活动并不是完全不可见。
Logcat图:
由于主活动并不是完全不可见,所以onStop()方法不会得到执行,所以只会执行onPause()方法。
5、按下返回键,回到主活动。
Logcat图:
同样的,返回主活动时,由于不是从停止态向运行态过度,所以只执行onResume()方法。重新获得输入焦点。
6、在主活动上,按下返回键结束程序。
Logcat图:
从运行态到结束活动,会依次执行暂停、停止、销毁方法。
另外的,活动被回收了怎么办。例:活动A启动了活动B,活动A进入了停止态,但是由于系统内存不足将A活动回收了,此时按下返回键,从B活动回到A活动会经历什么?
同样的道理,A活动被回收,从停止态直接销毁。执行onDestory()方法。回到A活动的过程中,A活动会被重新创建,从onCreate()方法开始执行,而不会执行到onRestart()
方法,所以,理解活动生命周期很重要,一定要避免重要数据未保留,而活动却被系统回收。
了解了活动的生命周期后,还有一些重点需要掌握。即活动的异常的生命周期。
导致异常的情况有两种:
1、系统配置发生变化导致活动被杀死并重新创建(多指横竖屏切换)。
2、由于内存不足导致处于停止态的低优先级的活动被杀死。
首先,从第一种情况开始说明,我们可以利用上面讲到的程序进行分析,首先在重写的7个方法上面再重写两个方法。如下:
//保存数据方法 @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Log.d(TAG,"onSaveInstanceState"); } //恢复数据方法 @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); Log.d(TAG,"onRestoreInstanceState"); }
这次执行程序需要模拟器模拟横竖屏切换。我使用的是Genymotion模拟器,同时按下Ctrl + F11即可实现横竖屏切换。
运行程序,并将竖屏切换为横屏。注意Loacat输出。
运行图:
此时Logcat图:
按下Ctrl + F11,切换为横屏。
此时Logcat图:
可以看到,活动被杀死后重新创建。暂停后执行保存数据方法,然后执行停止、销毁方法,然后创建活动,执行创建、开始方法,并执行恢复数据方法,然后执行onResume()方法获得输入焦点。
我们的预期是将横竖屏切换不会影响到活动的生命周期,而是从什么状态切换,到什么状态结束。上面的例子就是异常的活动生命周期。
然而我们绝对不希望活动的运行是这样的,到底要怎么避免旋转屏幕后活动销毁重建呢?那就是为活动设置configChange属性。即configChange:"orientation",在SDK>13时还要增加一个screenSize属性,要怎么使用呢?还是以上面的程序为例,在AndroidManifest.xml文件的主活动定义标签内加入下面一句话:
<activity android:name=".MainActivity" android:configChanges="orientation|screenSize" >
这些还不够,光一条语句不能实现预期,它的意义就是:横竖屏切换时不会让活动销毁重建,而是回调onConfigurationChanged()方法。在MainActivity中重写该方法。
//重写的配置改变方法 @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); Log.d(TAG,"onConfigurationChanged"); }
然后再次运行程序,Logcat中的输出如下(运行结果图及屏幕旋转过程略):
可以看到活动不会再销毁重建了,而是运行了onConfigurationChanged()方法。
第二种情况,系统由于内存不足而将处于停止态的低优先级活动杀死,这个其实就是活动生命周期被以外终止,依次执行暂停、保存数据、停止、销毁方法。我们要做的就是尽量保证活动的优先级高。
进程优先级分5种,优先级从高到低分别为:1、前台进程。2、可视进程。3、服务进程。4、后台进程。5、空进程。
尽量避免停止态中的活动不会被系统杀死的做法就是,尽量将活动所在的进程设置为前台进程,或者通过service绑定,也可以使其成为一个单独的进程。
本文总结参考自郭神(郭霖)的《第一行代码 第2版》以及zejian的活动解析博客。
相关文章推荐
- Android活动的生命周期之异常生命周期分析(三)
- Android活动的生命周期之正常生命周期分析(二)
- 对于Android Service 生命周期进行全解析
- 张高兴的 Xamarin.Android 学习笔记:(三)活动生命周期
- Android 程式开发:(一)详解活动 —— 1.1 Activity的生命周期
- 对于Android Service 生命周期进行全解析
- Android活动生命周期
- Android中采用XmlPullParser解析xml文件异常的问题
- 在Android2.3上运行app时出现json解析异常,而在4.0则没有
- Android7.0及以上android.os.FileUriExposedException,解析安装包出现异常
- Android学习笔记之(二):活动(3)活动的生命周期和启动方式
- Android学习(5)-活动的生命周期
- Android之路 1.activity跳转、传值、生命周期、活动
- Android 7.0 出现 ”FileUriExposedException“ 和 ”解析包出现错误“ 异常的解决办法
- Android全局异常捕获,不退出应用,让应用正常运行下去!
- 【Android】16、体验活动的生命周期(上)
- [转载] Android学习笔记之AndroidManifest.xml文件解析
- Android活动(Activity)状态与生命周期总结
- android 错误收集 SAX解析 解析正常,但是数据不能拿到
- Android之fragment生命周期解析