Android学习笔记:Android中活动的生命周期(正常和异常)解析

本文详细探讨了Android活动中正常和异常生命周期的各个阶段,包括onCreate(), onStart(), onResume(), onPause(), onStop(), onDestroy()和onRestart()等回调方法。通过实例展示了如何在横竖屏切换时避免活动销毁重建,以及如何处理内存不足时的活动管理。理解这些生命周期对于优化用户体验至关重要。" 122505541,11808297,JavaScript实现简易EventBus,"['javascript', '前端开发', '开发语言']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对活动的生命周期有了充分的了解才能对程序作出最好的优化,给用户最好的使用体验。

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的活动解析博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值