跳至主要內容

Activity的生命周期


什么是生命周期呢?

我们平时在开发软件的时候,就有软件的生命周期。同样的很多事物都有自己的生命周期。一般来说,生命周期不由自己控制。自杀是一个例外,对吧!

先举个例子吧: – 一辆车的生命周期有:被创建—>被购买—->被使用—–>被报废 – 一个人的生命周期:被成为受精卵—->被出生—–>被长大——>被去世

Snip20171209_4.png
Snip20171209_4.png

相信这两个例子可以理解吧,虽然举得不太好,是吧!!

而我们的Activity也是有生命周期的:

被创建—->被开始—>被可视—–>被暂停—–>被停止——>被销毁

先不用关心实际是怎么回事,把这文章读完你就知道了,实在不行就看视频吧。

Activity的生命周期

其实前面我们已经使用到了Activity的生命周期方法了,我们常在onCreate方法里头设置内容呢,找到控件之类的操作。

Snip20171209_5.png
Snip20171209_5.png

这个方法是被系统回调的,也就是不由我们控制的。学习Activity的生命周期有什么用呢?当然有用,我们先举个例子:

比如说我们前面有这样一个发短信的例子: 它的布局如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">


    <EditText
        android:id="@+id/message_content"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:hint="请输入短信内容!"
        android:inputType="textMultiLine"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="sendMsg"
        android:text="发送短信!"/>

</LinearLayout>
Snip20171209_9.png
Snip20171209_9.png

Activity里头什么都不写,就这样子:

package com.sunofbeaches.activitylifecircledemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

接着 ,把应用部署到模拟器上—->再输入一些内容—->点击返回键—–>再打开应用

Untitled6.gif
Untitled6.gif

我们很惊讶地发现,我们再次打开这个应用的时候,上次写的东西已经没有了。

如果想要用户体验好一点,是不是要把数据保存起来呢?那什么时候保存,当然是在销毁之前保存吧。

那被销毁就是一个生命周期方法啦!

Snip20171209_10.png
Snip20171209_10.png

这个onDetory是怎么回事呢?我们后面再对所有的生命周期方法进行详细的解释。

我们按前面的想法,我们要在onDetory方法里把数据保存起来,也就是点击返回键的以后保存数据。

当我们的应用再次跑起来的时候,它又会执行onCreate方法了。这样子就会去sp里拿数据,如果有就回显出来呗!

如果是忘记了如何保存数据的,请回去看前面有课程吧,这个是网易云的视频地址,免费的哦,欢迎大家给评价哦!

https://study.163.com/course/introduction/1004334020.htm

看看后面的效果吧:

package com.sunofbeaches.activitylifecircledemo;

import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity {

    private EditText mInputBox;
    private SharedPreferences mMsgConfig;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //前面我们学习数据存储的时候学习过了sp的存储,这里我们就使用sp来存储这些简单的数据即可
        mMsgConfig = this.getSharedPreferences("MsgConfig", MODE_PRIVATE);
        //输入框控件
        mInputBox = (EditText) this.findViewById(R.id.message_content);
        //在sp里拿到内容
        String content = mMsgConfig.getString("content", null);
        //如果内容不为空的话,再设置到输入框里去,显示出来。
        if (content != null) {
            mInputBox.setText(content);
        }
    }

    @Override
    protected void onDestroy() {
        //销毁之前,拿到输入框框的内容,然后判断是否为空,不为空的话保存起来,为下一次进入的时候显示出来。
        String content = mInputBox.getText().toString().trim();
        if (!TextUtils.isEmpty(content)) {
            mMsgConfig.edit().putString("content", content).commit();
        }
        super.onDestroy();
    }
}

测试结果如下:

Untitled61.gif
Untitled61.gif

我们可以发现,当点击返回键退出应用程序的时候数据保存起来了,当我们再次打开应用程序的时候,数据回显了。

Activity生命周期的微观细聊

前面我们了解了Activity的两个生命周期方法,分别是onCreate方法和onDestroy方法。

这两个方法分别会在Activity创建和销毁的时候被调用。

其实,Activity还有别的生命周期方法,但是我们会重onCreate开始详细学习。

下面这个是整个的生命周期流程图了,来自官网:

20171209_152816.png
20171209_152816.png

onCreate

onCreate()这个方法是在Activity被创建的时候调用的.在这个方法里头,我们一般做一些初始化的动作,比如说,设置和获取到UI的控件,设置对应的监听事件等。

我们看看官方文档是怎么说的吧:


    /**
     * Called when the activity is starting.  This is where most initialization
     * should go: calling {@link #setContentView(int)} to inflate the
     * activity's UI, using {@link #findViewById} to programmatically interact
     * with widgets in the UI, calling
     * {@link #managedQuery(android.net.Uri , String[], String, String[], String)} to retrieve
     * cursors for data being displayed, etc.
     *
     * <p>You can call {@link #finish} from within this function, in
     * which case onDestroy() will be immediately called without any of the rest
     * of the activity lifecycle ({@link #onStart}, {@link #onResume},
     * {@link #onPause}, etc) executing.
     *
     * <p><em>Derived classes must call through to the super class's
     * implementation of this method.  If they do not, an exception will be
     * thrown.</em></p>
     *
     * @param savedInstanceState If the activity is being re-initialized after
     *     previously being shut down then this Bundle contains the data it most
     *     recently supplied in {@link #onSaveInstanceState}.  <b><i>Note: Otherwise it is null.</i></b>
     *
     * @see #onStart
     * @see #onSaveInstanceState
     * @see #onRestoreInstanceState
     * @see #onPostCreate
     */

能看懂原文的同学看原文,看不懂的看这里吧: onCreate这个方法会在Activity启动的时候被调用。在这个启动的阶段,也就是在这个方法的内部,你可以去做一些初始化的动作。就是前面我们说到的初始话控件,初始化事件之类的。在这个方法里,也应该通过setContentView这个方法来设置Activity加载的UI内容。使用findViewById来找到这个UI里的各控件。

managedQuery这个先不用学,就算后面学的内容提供都也是很少用到的。这个方法已经过时了,现在用的是CursorLoader这个,大家先不用管这个。我们要知道的是onCreate里面做初始化动作,并且要知道的是,Activity没有被销毁,再次启动的时候是不会被执行的,比如说,我们点击Home键的时候,Activity只是退到后台,并没有销毁掉的。所以,再次启动的时候,是不会调用onCreate方法的。

如果你在这个方法里头调用finish()方法,那么系统就会直接调用onDestroy()方法了,它会跳过onStart()方 法,会跳过onResume()方法,也会跳过onPause()方法。

一般来说,我们不会在onCreate方法里调用finish()方法的。大家理解了吗?如果没有理解的话就发帖子讨论吧。

集成自Activity的类,实现了onCreate方法,必须要调用 super.onCreate(savedInstanceState)这个方法,否则会抛出异常。

20171209_141857.png
20171209_141857.png

会报什么异常呢?

20171209_142021.png
20171209_142021.png

所以上面的原文说,你要调用 super.onCreate(savedInstanceState);

接下来我们再看看这个参数:savedInstanceState 这个参数是用来做什么的呢?这里先说一下,这个其实是用来保存数据的。

一般情况下,这个saveInstanceState为空的,那什么时候它不为空呢?当点击home键的时候,系统就会调用onSaveInstanceState这个方法,你可以在这个方法里保存临时退出时要保存的内容,比如说下载状态呀,当前的进度之类的。

先看一下例子,理解一下吧:

package com.sunofbeaches.activitylifecircledemo;

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

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate...");
        if (savedInstanceState != null) {
            String saveData = savedInstanceState.getString("saveData");
            Log.d(TAG, "上次系统杀死时保存的内容... " + saveData);
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.d(TAG, "onSaveInstanceState...");
        outState.putString("saveData", "我是保存的数据");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy...");
    }
}

然后进行以下的操作:

启动这个应用,我们可以看到执行了onCreate方法,接着,点击home键,就发会现执行onSaveInstanceState这个方法,在这个方法里头,我们保存了一条数据。接着,我们模拟系统杀死这个应用,我们打开DDMS,直接选择当前的进程,然后stop掉,再次启动我们的应用时,就会发现,onCreate方法的时候 ,这个savedInstanceState就不为空了。并且保存着上次的数据,我们读出来。

20171209_151225.png
20171209_151225.png

这个onSaveInstanceState方法什么时候会执行呢?系统把你退到后台的时候会执行。 比如说:点击了home键盘;长按home键选择其他应用的时候,也就是切换应用;从当前的Activity启动到新的Activity里也会被调用;横竖屏的切换也会被调用,这里的话仅作理解,初学者暂时不需要深入学习。后面实际开发的时候,我们讲项目的时候,我们就会进行讲解了。什么场景下使用到。

好啦,我们onCreate方法先讲这么多吧。

要知道这个是生命周期方法,并且知道在这个方法里面做什么就可以了,还要注意的是onCreate要调用super的方法,否则会崩溃。这个的话不用担心,IDE在创建Activity的时候,你复写onCreate方法,直接会帮你写上的。

onStart

onStart方法的官方文档是这么写的:

  /**
     * Called after {@link #onCreate} — or after {@link #onRestart} when
     * the activity had been stopped, but is now again being displayed to the
     * user.  It will be followed by {@link #onResume}.
     *
     * <p><em>Derived classes must call through to the super class's
     * implementation of this method.  If they do not, an exception will be
     * thrown.</em></p>
     *
     * @see #onCreate
     * @see #onStop
     * @see #onResume
     */

这个方法会在onCreate方法以后调用,或者会在onRestart这个方法调用以后被调用,看到这里,返回去看看前面的Activity声明周期流程图吧:

20171209_153108.png
20171209_153108.png

同样的,也要和onCreate方法一样,需要调用super方法哦。这个方法一般比较少用到。大家后面看视频的时候留意一下就可以了。

onResume

到了onResume方法,官方原文:

   /**
     * Called after {@link #onRestoreInstanceState}, {@link #onRestart}, or
     * {@link #onPause}, for your activity to start interacting with the user.
     * This is a good place to begin animations, open exclusive-access devices
     * (such as the camera), etc.
     *
     * <p>Keep in mind that onResume is not the best indicator that your activity
     * is visible to the user; a system window such as the keyguard may be in
     * front.  Use {@link #onWindowFocusChanged} to know for certain that your
     * activity is visible to the user (for example, to resume a game).
     *
     * <p><em>Derived classes must call through to the super class's
     * implementation of this method.  If they do not, an exception will be
     * thrown.</em></p>
     *
     * @see #onRestoreInstanceState
     * @see #onRestart
     * @see #onPostResume
     * @see #onPause
     */

看前面的流程图,我们可以知道,这个方法会在onRestoreInstanceState方法或者onRestart方法或者onPause这个方法以后被调用。这个方法以后就可以和用户进行交互了。在这个方法里面开始动画是不错的做法,打开相机等等操作…可是我没有做过,哈哈!!!只是翻译原文。

这个方法在实际开发中,用得最多的是什么情况呢?

就是可见的时候,需要做一些动画,比如说,我以前在做手表的时候,有这样一个效果:

adjustNumber1.gif
adjustNumber1.gif

直接创建不算典型的,典型的是onResume跟onCreate方法有一个区别,onCreate方法如果不销毁掉是不会再执行的,但是onResume,你退到了后台以后,再回来就会执行这个方法了。

要注意的是在onResume这个方法并不一定保证当前Activity在前台的,比如说键盘已经显示出来了。这个时候,当前Activity是失去焦点的,所以,可以使用onWindowFocusChanged这个方法来判断当前Activity是否可见。

同样的,这个方法也要调用super,否则还是会崩溃掉的。

onPause

onPause这个方法是暂停的意思是吧,暂停,什么时候会调用呢?失去焦点了

fter the next activity has been resumed and
     * displayed), however in some cases there will be a direct call back to
     * {@link #onResume} without going through the stopped state.
     *
     * <p><em>Derived classes must call through to the super class's
     * implementation of this method.  If they do not, an exception will be
     * thrown.</em></p>
     *
     * @see #onResume
     * @see #onSaveInstanceState
     * @see #onStop
     */

这个方法也是声明周期方法,它会在当前Activity进入后台但又没被杀死的时候会被调用。它对应着onResume,其实我们的声明周期一般来说是对应着的。 比如说:

onStart—-onStop onResume—onPause onCreate—-onDestroy

理解了吗?

在这个方法里不要做耗时操作,假设说,你要打开另外一个Activity,真到这个方法的onPause方法返回了,才会去启动另外一个Activity。

在这个方法里头,一般保存正在编辑的数据,除了保存数据以外,还可以停止动画,因为它是跟onResume对应的嘛。在onResume开始动画,我们要是还有一些在循环播放的动画,我们可以在这个方法里头暂停掉它。在这个方法里头释放资源也是可以的,释放掉资源可以更快地启动到下一个界面。

某些情况下系统可能会回收资源。所以有些东西你需要在这个地方进行保存。通常来说,如果是系统干的坏事,它会调用onSaveInstanceState这个方法的,在这个方法里头保存就好,前面我们已经讲过了。

这个方法被调用以后,当被启动的Activity可见的时候,接下来就会调用onStop方法了。但是有些情况下直接调用新的Activity的onResume方法,不会调用当前Activity的onStop方法.

这个方法也要调用super的方法,否则也会崩溃掉。

onStop

停止的回调,这个方法一般是执行完onPause,要退出了,则会调用。假设说,顶部有一个透明的Activity,则不会调用当前的onStop方法,但会调用到onPause方法。

 /**
     * Called when you are no longer visible to the user.  You will next
     * receive either {@link #onRestart}, {@link #onDestroy}, or nothing,
     * depending on later user activity.
     *
     * <p>Note that this method may never be called, in low memory situations
     * where the system does not have enough memory to keep your activity's
     * process running after its {@link #onPause} method is called.
     *
     * <p><em>Derived classes must call through to the super class's
     * implementation of this method.  If they do not, an exception will be
     * thrown.</em></p>
     *
     * @see #onRestart
     * @see #onResume
     * @see #onSaveInstanceState
     * @see #onDestroy
     */

onStop方法会在你再也看不到当前Activity的时候调用,当上面的Activity是透明你的时候,那么是不会调用的,这个是考点哦。

这个方法以有两个可能,要么被onRestart,要么就被调用onDestroy,或者啥也不干,这取决于后面的操作。有些情况下这个方法可能不会被执行。比如说系统的内存不足以来跑当前的Activity的时候,调用了onPause就不调用onStop方法了,所以在这个方法里头不要做一些保存数据的东西,可能不安全,没有保障.

onDestroy

onDestroy这个方法,我们早在前面就已经用了是吧,我们保存内容,并且在onCreate的方法我们回显数据。

 /**
     * Perform any final cleanup before an activity is destroyed.  This can
     * happen either because the activity is finishing (someone called
     * {@link #finish} on it, or because the system is temporarily destroying
     * this instance of the activity to save space.  You can distinguish
     * between these two scenarios with the {@link #isFinishing} method.
     *
     * <p><em>Note: do not count on this method being called as a place for
     * saving data! For example, if an activity is editing data in a content
     * provider, those edits should be committed in either {@link #onPause} or
     * {@link #onSaveInstanceState}, not here.</em> This method is usually implemented to
     * free resources like threads that are associated with an activity, so
     * that a destroyed activity does not leave such things around while the
     * rest of its application is still running.  There are situations where
     * the system will simply kill the activity's hosting process without
     * calling this method (or any others) in it, so it should not be used to
     * do things that are intended to remain around after the process goes
     * away.
     *
     * <p><em>Derived classes must call through to the super class's
     * implementation of this method.  If they do not, an exception will be
     * thrown.</em></p>
     *
     * @see #onPause
     * @see #onStop
     * @see #finish
     * @see #isFinishing
     */

在这个方法里做释放资源的动作,比如说,取消一些广播的注册,解绑服务。不要在这个方法里保存数据哦,前面那个例子是为了给大家演示。在onPause方法保存数据或者在onSaveInstanceState方法里保存数据。就是不要在这里保存数据就好。这个方法通常用于释放资源,比如说释放线程,释放掉的话就不会泄露了。有些情况下不会调用这个方法,所以这里不要使用一直在跑的进程。

这个方法也要调用super的方法,否则的话会崩溃掉的。

onPause和onStop

我们前面详细地翻译和解释了每个生命周期的特点,不管是同学们在看视频,还是看文章自己做实验。我们会发现当我们点击Home键的时候,生命周期会执行–>onPause—>onStop。

前面我们也提到了,onPause是失去焦点的意思,对应着onResume获得焦点。

onStop是不可见了,所以。我们的Activity先是失去焦点,再是不可见!于是就有了这样的生命周期:onPause–>onStop

但是,面试的时候可能会有一种特殊的情况:我们的当前Activity,去启动另外一个透明的Activity时,当前的Activity生命周期变化是怎么样的?

答案是:onPause,没有onStop。因为是透明的,所以前一个Activity还是可见的。只是失去焦点而已,并不是不可见!

验证代码如下:

先是第一个Activity:

package com.sunofbeaches.activitylifecircledemo;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

/**
 * Created by TrillGates on 17/12/12.
 * God bless my code!
 */
public class FirstActivity extends Activity {
    private static final String TAG = "FirstActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_first);
        Log.d(TAG, "onCreate....");
    }

    public void skip2SecondActivity(View view) {
        Intent intent = new Intent(this, SecondActivity.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....");
    }
}

第一个Activity的布局很简单:

Snip20171212_15.png
Snip20171212_15.png

第二个Activity的代码,其实什么都没有写:

package com.sunofbeaches.activitylifecircledemo;

import android.app.Activity;
import android.os.Bundle;

/**
 * Created by TrillGates on 17/12/12.
 * God bless my code!
 */
public class SecondActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
    }
}

界面:

Snip20171214_17.png
Snip20171214_17.png

我们设置一下第二个样式为透明:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
          package="com.sunofbeaches.activitylifecircledemo">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">

        </activity>
        <activity android:name=".FirstActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity
            android:name=".SecondActivity"
            android:theme="@android:style/Theme.Translucent"></activity>
    </application>

</manifest>

当我们把应用跑起来,点击跳转到第二个界面,我们看生命周期的变化。

Snip20171214_18.png
Snip20171214_18.png

当我们把应用跑起来,点击跳转到第二个界面,我们看生命周期的变化。

Activity生命周期的分类

完整的声明周期:

也就是说,从onCreate开始,一直到onDestroy方法执行完成。这就是一个完整的声明周期。 一般来说,完整的声明周期走完所有的声明周期方法。在onCreate方法的时候初始化资源,在onDestroy方法释放资源。

Activity可见的声明周期:

可见的声明周期就是调用onStart到调用onStop这段声明周期。在这其间,用户可以看到UI,也可以进行交互。在这两个方法之间,你可以让要显示的数据显示给用户。栗子我就不说了。

前台生命周期:

什么是前台呢?也就是正在操作的,相对于后台运行来说的。前台生命周期是从onResume到onPause这期间。在这期间的话,Activity会跑在前台。Activity可能会频繁地切换于onResume和onPause这两个方法间。前面我们讲到了,onResume是获取到了焦点了,onPause就失去焦点了。

原文的内容如下:

20171213_165921.png
20171213_165921.png

Activity的生命周期宏观概括

声明周期就像人的各个阶段,但是有些阶段不是每个人都经历的,而重点是在对应的阶段做对应的事情。这就是总结啦!

实际的使用,还得从实际开发中去体会,这节课了解一下先,在以后的工作中就慢慢地理解了。但是呢,这样子还不够的,因为出来工作的时候,面试可能会遇到哦。

关于Activity声明周期的面试

三年前,我去一家公司应聘的时候就考到了Activity的声明周期。

它大概是这样子的,给你一个场景,写出Activity的声明周期变化。

比如说:当前Activity在前台运行,点击了home键,退到后在, Activity的声明周期是怎么样的?

当前Activity启动了一个透明的Activity,此过程Activity的声明周期变化。

横屏切换成竖屏声明周期的变化!

请画出Activity声明周期的流程图。

没什么意外的话,视频会在这周日出,今天周一,12月11号。

相关的阅读:

Android官方文档的Activity文档:

https://developer.android.com/reference/android/app/Activity.html

在线视频教程

https://www.sunofbeach.net/c/1443881236287311874