您的位置:首页 > 移动开发 > Android开发

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 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的活动解析博客
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: