[Android]Activity的生命周期

想要学好安卓开发,就必须理解安卓软件的生命周期。明确一个活动的创建、启动、停止、暂停、重新启动和销毁的过程,知道各个阶段会调用什么函数进行处理不同的情况。这里我们就来说说Activity的生命周期。

1. 活动栈

Android 中的活动是层叠的,我们每启动一个新的活动,就会覆盖在原活动之上。然后点击 Back 键会销毁最上面的活动,以下的一个活动就会又一次显示出来。

事实上 Android 是使用任务来管理活动的。一个任务就是一组存放在栈里的活动的集合。这个栈也被称作活动栈 。

栈是一种后进先出的数据结构。在默认情况下。每当我们启动了一个新的活动。它会在返回栈中入栈,并处于栈顶的位置。而每当我们按下 Back 键或调用 finish()方法去销毁一个活动时,处于栈顶的活动会出栈。这时前一个入栈的活动就会又一次处于栈顶的位置。

系统总是会显示处于栈顶的活动给用户。

2. 活动的状态

每一个活动在其生命周期中最多可能会有四种状态。

  1. 执行状态
    当一个活动位于活动栈的栈顶时。这时活动就处于执行状态。系统最不愿意回收的就是处于执行状态的活动,由于这会带来非常差的用户体验。

  2. 暂停状态
    当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态。

    你可能会认为既然活动已经不在栈顶了,还怎么会可见呢?这是由于并非每一个活动都会占满整个屏幕的。比方对话框形式的活动仅仅会占用屏幕中间的部分区域,你非常快就会在后面看到这样的活动。处于暂停状态的活动仍然是全然存活着的。系统也不愿意去回收这样的活动(由于它还是可见的。回收可见的东西都会在用户体验方面有不好的影响) 。仅仅有在内存极低的情况下。系统才会去考虑回收这样的活动。

  3. 停止状态
    当一个活动不再处于栈顶位置。而且全然不可见的时候,就进入了停止状态。

    系统仍然会为这样的活动保存对应的状态和成员变量。可是这并非全然可靠的,当其它地方须要内存时,处于停止状态的活动有可能会被系统回收。

  4. 销毁状态
    当一个活动从活动栈中移除后就变成了销毁状态。系统会最倾向于回收处于这样的状态的活动,从而保证手机的内存充足。

3. 活动的生存期

Activity 类中定义了七个回调方法。覆盖了活动生命周期的每一个环节,以下我来一一介绍下这七个方法。
1. onCreate()
这种方法你已经看到过非常多次了,每一个活动中我们都重写了这种方法,它会在活动第一次被创建的时候调用。

你应该在这种方法中完毕活动的初始化操作,比方说载入布局、绑定事件等。


2. onStart()
这种方法在活动由不可见变为可见的时候调用。
3. onResume()
这种方法在活动准备好和用户进行交互的时候调用。此时的活动一定位于活动栈的栈顶,而且处于执行状态。
4. onPause()
这种方法在系统准备去启动或者恢复还有一个活动的时候调用。我们一般会在这种方法中将一些消耗 CPU 的资源释放掉,以及保存一些重要数据,但这种方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。
5. onStop()
这种方法在活动全然不可见的时候调用。

它和 onPause()方法的主要差别在于,假设启动的新活动是一个对话框式的活动。那么 onPause()方法会得到执行,而 onStop()方法并不会执行。


6. onDestroy()
这种方法在活动被销毁之前调用,之后活动的状态将变为销毁状态。
7. onRestart()
这种方法在活动由停止状态变为执行状态之前调用,也就是活动被又一次启动了。

以上七个方法中除了 onRestart()方法。其它都是两两相对的。从而又能够将活动分为三种生存期。
1. 完整生存期
活动在 onCreate()方法和 onDestroy()方法之间所经历的,就是完整生存期。普通情况下。一个活动会在 onCreate()方法中完毕各种初始化操作,而在 onDestroy()方法中完毕释放内存的操作。


2. 可见生存期
活动在 onStart()方法和 onStop()方法之间所经历的。就是可见生存期。在可见生存期内,活动对于用户总是可见的,即便有可能无法和用户进行交互。我们能够通过这两个方法。合理地管理那些对用户可见的资源。比方在 onStart()方法中对资源进行载入,而在 onStop()方法中对资源进行释放, 从而保证处于停止状态的活动不会占用过多内存。
3. 前台生存期
活动在 onResume()方法和 onPause()方法之间所经历的,就是前台生存期。在前台生存期内。活动总是处于执行状态的,此时的活动是能够和用户进行相互的,我们平时看到和接触最多的也这个状态下的活动。
这里写图片描写叙述

4. 活动生命期实例

讲了这么多理论知识,也是时候该实战一下了,以下我们将通过一个实例,让你能够更加直观地体验活动的生命周期。

这次我们不准备在 ActivityTest 这个项目的基础上改动了,而是新建一个项目。

因此,首先关闭 ActivityTest 项目。然后新建一个ActivityLifeCycleTest 项目。

新建项目的过程你应该已经非常清楚了,不须要我再进行赘述,这次我们同意 ADT 帮我们自己主动创建活动。这样能够省去不少工作,创建的活动名和布局名都使用默认值。这样主活动就创建完毕了,我们还须要分别再创建两个子活动,NormalActivity 和DialogActivity,以下一步步来实现。

新建 normal_layout.xml 文件,代码例如以下所看到的:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="This is a normal activity"
    />
</LinearLayout>

这个布局中我们就非常easy地使用了一个 TextView,然后相同的方法,我们再新建一个 dialog_layout.xml 文件,代码例如以下所看到的:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="This is a dialog activity"
    />
</LinearLayout>

两个布局文件的代码差点儿没有差别,仅仅是显示的文字不同而已。然后新建 NormalActivity 继承自 Activity,代码例如以下所看到的:

public class NormalActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.normal_layout);
    }
}

我们在 NormalActivity 中载入了 normal_layout 这个布局。相同的方法。再新建 DialogActivity 继承自 Activity,代码例如以下所看到的:

public class DialogActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.dialog_layout);
    }
}

我们在 DialogActivity 中载入了 dialog_layout 这个布局。事实上从名字上你就能够看出。这两个活动一个是普通的活动,一个是对话框式的活动。可是如今无论怎么看,这两个活动的代码都差点儿都是一模一样的,在哪里有体现出将活动设成对话框式的呢?别着急,以下我们立即開始设置。在AndroidManifest.xml 的标签中加入例如以下代码:

<activity android:name=".NormalActivity" >
</activity>

<activity android:name=".DialogActivity" android:theme="@android:style/
Theme.Dialog" >
</activity>

这里分别为两个活动进行注冊。可是 DialogActivity 的注冊代码有些不同,它使用了一个 android:theme 属性,这是用于给当前活动指定主题的。Android 系统内置有非常多主题能够选择。当然我们也能够定制自己的主题。而这里@android:style/Theme.Dialog 则毫无疑问是让DialogActivity 使用对话框式的主题。

接下来我们改动 activity_main.xml,又一次定制我们主活动的布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    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>

自己主动生成的布局代码有些复杂, 这里我们全然替换掉。 仍然还是使用最熟悉的 LinearLayout。然后加入了两个button,一个用于启动NormalActivity,一个用于启动 DialogActivity。

最后改动 MainActivity 中的代码,例如以下所看到的:

public class MainActivity extends Activity {
    public static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate");

        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        Button startNormalActivity = (Button) findViewById(R.id.start_normal_activity);
        Button startDialogActivity = (Button) findViewById(R.id.start_dialog_activity);

        startNormalActivity.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(MainActivity.this,
            NormalActivity.class);
            startActivity(intent);
        }
        });

        startDialogActivity.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(MainActivity.this,
            DialogActivity.class);
            startActivity(intent);
        }
        });
    }

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

在onCreate()方法中,我们分别为两个button注冊了点击事件,点击第一个button会启动NormalActivity,点击第二个button会启动 DialogActivity。

然后在 Activity 的七个回调方法中分别打印了一句话,这样就能够通过观察日志的方式来更直观地理解活动的生命周期。

如今执行程序。效果如图所看到的:
这里写图片描写叙述

5. 活动被销毁了怎么办

前面我们已经说过,当一个活动进入到了停止状态,是有可能被系统回收的。那么想象以下场景,应用中有一个活动 A,用户在活动 A 的基础上启动了活动 B。活动 A 就进入了停止状态。这个时候由于系统内存不足。将活动 A 回收掉了,然后用户按下 Back 键返回活动 A, 会出现什么情况呢?事实上还是会正常显示活动 A的, 仅仅只是这时并不会执行 onRestart()方法。而是会执行活动 A 的 onCreate()方法,由于活动 A 在这样的情况下会被又一次创建一次。

这样看上去好像一切正常,可是别忽略了一个重要问题,活动 A 中是可能存在暂时数据和状态的。

打个比方,MainActivity 中有一个文本输入框。如今你输入了一段文字。然后启动 NormalActivity,这时 MainActivity 由于系统内存不足被回收掉。过了一会你又点击了Back 键回到 MainActivity。你会发现刚刚输入的文字所有都没了。由于 MainActivity 被又一次创建了。

假设我们的应用出现了这样的情况。是会严重影响用户体验的,所以必须要想想办法解决问题。查阅文档能够看出。Activity 中还提供了一个 onSaveInstanceState()回调方法。这种方法会保证一定在活动被回收之前调用。因此我们能够通过这种方法来解决活动被回收时暂时数据得不到保存的问题。

onSaveInstanceState()方法会携带一个 Bundle 类型的參数。Bundle 提供了一系列的方法用于保存数据。比方能够使用 putString()方法保存字符串。使用 putInt()方法保存整型数据。以此类推。每一个保存方法须要传入两个參数,第一个參数是键,用于后面从 Bundle 中取值。第二个參数是真正要保存的内容。

在 MainActivity 中加入例如以下代码就能够将暂时数据进行保存:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    String tempData = "Something you just typed";
    outState.putString("data_key", tempData);
}

数据是已经保存下来了。那么我们应该在哪里进行恢复呢?细心的你或许早就发现,我们一直使用的 onCreate()方法事实上也有一个 Bundle 类型的參数。

这个參数在普通情况下都是null,可是当活动被系统回收之前有通过 onSaveInstanceState()方法来保存数据的话,这个參数就会带有之前所保存的所有数据。我们仅仅须要再通过对应的取值方法将数据取出就可以。

改动 MainActivity 的 onCreate()方法,例如以下所看到的:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.d(TAG, "onCreate");
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.activity_main);

    if (savedInstanceState != null) {
        String tempData = savedInstanceState.getString("data_key");
        Log.d(TAG, tempData);
    }
    ……
}

取出值之后再做对应的恢复操作就能够了,比方说将文本内容又一次赋值到文本输入框上,这里我们仅仅是简单地打印一下。

不知道你有没有察觉,使用 Bundle 来保存和取出数据是不是有些似曾相识呢?没错!

我们在使用 Intent 传递数据时也是用的相似的方法。这里跟你提醒一点,Intent 还能够结合Bundle 一起用于传递数据的,首先能够把须要传递的数据都保存在 Bundle 对象中。然后再将 Bundle 对象存放在 Intent 里。到了目标活动之后先从 Intent 中取出 Bundle,再从 Bundle中一一取出数据。

原文地址:https://www.cnblogs.com/jhcelue/p/7363700.html