Activity生命周期

 

Posted by     DH on February 22, 2019

概述

Android的Activity组件生命周期包含正常生命周期和异常情况下的生命周期。

正常生命周期

正常情况下,Activity包含7个生命周期:

(1)onCreate:Activity正在创建,是整个Activity生命周期的第一个调用方法。在这个可以做一些子控件的初始化、加载页面布局、初始化数据等工作;

(2)onStart:Activity正在被启动,这个时候,Activity已经可见,但是在后台,还不能和用户交互,对用户来说是不可见的;

(3)onResume:Activity来到前台,用户可见可交互;

(4)onPause:Activity正在停止,很快就会调用onStop。这个时候如果手速过快,快速回到当前的Activity,那么便不会调用onStop,而是会调用onResume,当前Activity再次来到前台,变得可交互。但是这种情况一般是不可复现的。在这个方法中,不能做耗时操作,一般可以存储数据、停止动画。如果太耗时,会影响新的Activity的创建,因为只有当前Activity的onPause的执行完,新的Activity的onResume才会执行;

(5)onStop:Activity即将停止。这个时候可以进行一些稍微耗时的回收操作,但是也不能太耗时;

(6)onDestroy:Activity即将被销毁。是整个Activity生命周期的最后一个调用方法。在这个方法中,要回收和释放资源。 还有一个正常生命周期比较特殊,是Activity从后台来到前台时被调用的:

(7)onRestart:Activity正在重新启动,Activity从不可见到可见就会调用这个方法。出现调用这个方法的情况一般是用户回到桌面或者打开新的Activity后,调用了onPause和onStop之后,再回到这个Activity,就会调用onRestart。

正常情况下的生命周期就包含这七个,如下图所示:

从上图可以看出,正常生命周期是两两配对的,其中:

1️⃣onCreate和onDestroy配对,分别表示Activity的创建和销毁,只会被调用一次。

2️⃣onStart和onPause配对,根据用户操作和屏幕的点亮熄灭会被多次调用。

3️⃣onResume和onStop配对,根据用户操作和屏幕的点亮熄灭会被多次调用。

那么,用户的各种操作分别对应着Activity生命周期的那些方法呢?

(1)用户回到桌面、用户打开新的Activity:调用onPause——>onStop。这里注意,加入当前的Activity使用的主题是透明主题,那么不会调用onStop;

(2)用户回到原Activity:onRestart——>onStart——>onResume;

(3)点击返回按钮:调用当前Activity的onPause——>onStop——>onDestroy

异常情况的生命名周期

异常情况下的生命周期包括两种,1️⃣是系统资源配置变化,导致Activity销毁和重建;2️⃣是系统内存不足,导致Activity被回收,这种情况和Activity的优先级有关。

系统资源配置变化

某些系统资源配置变化会导致Activity被回收,然后重建。这里以屏幕旋转为例(当然也可以通过设置,阻止这种Activity的销毁和重建),系统配置发生了变化,Activity随之被销毁和重建。这个过程和正常的Activity的生命周期就发生了改变,Activity的onPause——>onStop——>onDestroy均会被调用,并且系统还会产生一个回调onSaveInstanceState(),这个方法用于保存之前Activity的一些状态信息,例如EditText中输入的文字,我们也可以在这个方法中手动的保存一些信息。需要注意的是onSaveInstanceState()方法调用的时机确定在onStop之前,但是和onPause的前后关系不确定。

其后,Activity会进行重建,在Activity调用onStart方法之后,会调用onRestoreInstanceState(),这个方法接收来自onSaveInstanceState()传递过来的一个bundle对象,该对象包含了之前Activity的信息。这个bundle对象会同时传递给onCreate()函数。因此,可以在onCreate函数和onRestoreInstanceState中取得数据,进行恢复。但是要注意的是,在onCreate中这个bundle对象有可能为空,正常生命周期这个bundle对象就为空,因此如果要在onCreate中恢复数据,一定要判断bundle对象是否为空,而在onRestoreInstanceState中则不用判空,因为产生onRestoreInstanceState这个回调,说明已经是异常生命周期。对应生命周期如下图所示:

我们模拟一下,这个过程:

package com.example.daihao.chap1test;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.util.Printer;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity {

    private EditText mEditText;
    private String TAG = "MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Log.d(TAG,"onCreate");

        setContentView(R.layout.activity_main);
        mEditText = findViewById(R.id.EV);

        if (savedInstanceState != null){
            // 获取手动保存的数据
            String testStr = savedInstanceState.getString("TestData");
            Log.d("MainActivity","oncreate获取的手动存储的数据:" + testStr);
        }

    }

    @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");
    }

    /**
     * 当系统资源发生变化,activity需要销毁重建时,调用此函数保存保存当前屏幕中数据
     * @param outState
     */
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.d("MainActivity","onSaveInstanceState存储数据:");
        // 手动保存数据
        outState.putString("TestData","测试数据");
    }

    /**
     * 接收来自onSaveInstanceState传入的存有数据的bundle对象,在新的activity创建的时候恢复
     * @param savedInstanceState
     */
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        // 获取手动保存的数据
        String testStr = savedInstanceState.getString("TestData");
        Log.d("MainActivity","onRestoreInstanceState获取的手动存储的数据:" + testStr);
    }
}

在页面中,我们加入了一个EditText控件。我们旋转屏幕之后,可以发现系统自动为我们保存了输入框中的数据:

而观察Activity的生命周期,如下:

内存不足导致Activity被回收

内存资源不足时,Activity会按照从低到高的顺序被回收,其中包含三种优先级:

(1)前台Activity,可见的、可交互的Activity,优先级最高,最后被回收;

(2)非前台可见Activity,例如位于弹窗后,可以看见的Activity,优先级次之;

(3)后台Activity,不可见,并且暂停的Activity,优先级最低,最先被回收。

系统会按照优先级最低的先回收,同时调用onSaveInstanceState保存数据,当Activity再次被创建的时候,通过onRestoreInstanceState获取数据并恢复。

防止系统配置变化引起Activity异常生命周期

能够引起Activity异常生命周期的配置很多,要阻止这种变化,需要给Activity指定configChanges属性,目前常用的属性包括locale(一般是切换系统语音引起)、orientation(屏幕旋转)、keyboardHidden(键盘访问性发生变化)。 假如我们要同时阻止locale(一般是切换系统语音引起)、orientation(屏幕旋转)引起的Activity异常生命周期,就要在AndroidMeniFest.xml中加入Activity声明:

这个时候,旋转屏幕并不是调用onSaveInstanceState保存数据和onRestoreInstanceState获取数据并恢复。