Android:Activity——生命週期深入詳解

一、生命週期全面分析


Android活動默認運行在當前進程所擁有的棧中,前臺可見的活動則在活動棧的最頂部。其他後臺活動則在棧的裏面,在正常的情況下(內存充足)其他的活動並沒有被回收或者殺死,它們仍然存在於棧中保持着原來的狀態。當前面的活動退出後,後面的活動就會搬到前臺使得被用戶可見。如果在非正常情況下(內存緊張、按下Home鍵後右啓動其他應用)那麼棧內的非前臺Activity就可能被回收,但是當我們返回到該Activity時它又會被重新構造,並且會通過onSaveInstance和onRestoreInstance加載原來的數據使得它保持之前的狀態呈現給用戶。無論是正常情況,還是非正常情況下,這樣的實現都是基於一個非常重要的機制——生命週期。


1.什麼是生命週期


週期即活動從開始到結束所經歷的各種狀態。生命週期即活動從開始到結束所經歷的各個狀態。從一個狀態到另一個狀態的轉變,從無到有再到無,這樣一個過程中所經歷的狀態就叫做生命週期。

  • Acitivity本質上有四種狀態

    1. 運行:如果一個活動被移到了前臺(活動棧頂部)。
    2. 暫停:如果一個活動被另一個非全屏的活動所覆蓋(比如一個Dialog),那麼該活動就失去了焦點,它將會暫停(但它仍然保留所有的狀態和成員信息,並且仍然是依附在WindowsManager上),在系統內存積極缺乏的時候會將它殺死。
    3. 停止:如果一個活動被另一個全屏活動完全覆蓋,那麼該活動處於停止狀態(狀態和成員信息會保留,但是Activity已經不再依附於WindowManager了)。同時,在系統缺乏資源的時候會將它殺死(它會比暫停狀態的活動先殺死)。
    4. 重啓:如果一個活動在處於停止或者暫停的狀態下,系統內存缺乏時會將其結束(finish)或者殺死(kill)。這種非正常情況下,系統在殺死或者結束之前會調用onSaveInstance()方法來保存信息,同時,當Activity被移動到前臺時,重新啓動該Activity並調用onRestoreInstance()方法加載保留的信息,以保持原有的狀態。

在上面的四中常有的狀態之間,還有着其他的生命週期來作爲不同狀態之間的過度,用於在不同的狀態之間進行轉換,生命週期的具體說明見下。


二、各個生命週期狀態的說明


1.正常情況下的生命週期

  1. onCreate:與onDestroy配對,表示Activity正在被創建,這是生命週期的第一個方法。在這個方法中可以做一些初始化的工作(加載佈局資源、初始化Activity所需要的數據等),耗時的工作在異步線程上完成。

  2. onRestart:表示Activity正在重新啓動。一般情況下,在當前Activity從不可見重新變爲可見的狀態時onRestart就會被調用。這種情形一般是由於用戶的行爲所導致的,比如用戶按下Home鍵切換到桌面或者打開了一個新的Activity(這時當前Activity會暫停,也就是onPause和onStop被執行),接着用戶有回到了這個Activity,就會出現這種情況。

  3. onStart:與onStop配對,表示Activity正在被啓動,並且即將開始。但是這個時候要注意它與onResume的區別。兩者都表示Activity可見,但是onStart時Activity還正在加載其他內容,正在向我們展示,用戶還無法看到,即無法交互。

  4. onResume:與onPause配對,表示Activity已經創建完成,並且可以開始活動了,這個時候用戶已經可以看到界面了,並且即將與用戶交互(完成該週期之後便可以響應用戶的交互事件了)。

  5. onPause:與onResume配對,表示Activity正在暫停,正常情況下,onStop接着就會被調用。在特殊情況下,如果這個時候用戶快速地再回到當前的Activity,那麼onResume會被調用(極端情況)。一般來說,在這個生命週期狀態下,可以做一些存儲數據、停止動畫的工作,但是不能太耗時,如果是由於啓動新的Activity而喚醒的該狀態,那會影響到新Activity的顯示,原因是onPause必須執行完,新的Activity的onResume纔會執行。

  6. onStop:與onStart配對,表示Activity即將停止,可以做一些稍微重量級的回收工作,同樣也不能太耗時(可以比onPause稍微好一點)。

  7. onDestroy:與onCreate配對,表示Activity即將被銷燬,這是Activity生命週期的最後一個回調,我們可以做一些回收工作和最終的資源釋放(如Service、BroadReceiver、Map等)。

    正常情況下,Activity的常用生命週期就是上面的7個,下圖更加詳細的描述的各種生命週期的切換過程:

    這裏寫圖片描述

這裏要說的是,從上圖我們可以看到一個現象:
onStart與onStop、onResume與onPause是配對的。兩種Activity回到前臺的方式,從onPause狀態回到前臺會走到onResume狀態,從onStop狀態回到前臺會到onStart狀態,這是從是否可見和是否在前臺來說的。從是否可見來說,onStart和onStop是配對的;從是否在前臺來說,onResume和onPause是配對的。至於爲什麼會有他們,在第三點生命週期的使用會說到。

我們來看看正常情況下生命週期的系統日誌:

03-23 00:15:52.970 32278-32278/com.example.david.lifecircle E/TAG: onCreate() is invoked!
03-23 00:15:52.971 32278-32278/com.example.david.lifecircle E/TAG: onStart() is invoked!
03-23 00:15:52.971 32278-32278/com.example.david.lifecircle E/TAG: onResume() is invoked!
03-23 00:15:55.858 32278-32278/com.example.david.lifecircle E/TAG: onPause() is invoked!
03-23 00:16:02.573 32278-32278/com.example.david.lifecircle E/TAG: onRestart() is invoked!
03-23 00:16:02.573 32278-32278/com.example.david.lifecircle E/TAG: onStart() is invoked!
03-23 00:16:02.573 32278-32278/com.example.david.lifecircle E/TAG: onResume() is invoked!

2.異常情況下的生命週期

一般正常情況的聲明週期就像上面所說的一樣,但是因爲Android本身內存或者其他的一些情況會使得Activity不按照正常的生命週期。比如當資源配置發生改變、系統內存不足時,Activity就會被殺死。下面分析這兩種常見的情況。
  • 情況1:資源相關的系統配置發生改變導致Activity被殺死並重新創建

    理解這個問題,我們首先要對系統的資源加載機制有一定了解,不過這裏我不分析系統資源加載機制了(因爲我也不怎麼懂)。簡單說明一下,就像是我們把一張圖片放在drawable目錄之後,就可以通過Resources去獲取這張圖片。同時爲了兼容不同的設備,我們還可能需要在其他的一些目錄放置不同的圖片,比如 drawable-mdpi、drawable-hdpi等。這樣,當應用程序啓動時,系統就會根據當前設備的情況去加載合適的Resource資源,比如說橫屏和豎屏的手機會拿到兩張不同的圖片(設定了landscape或portrait狀態下的圖片)。
    如果說,當前Activity處於豎屏狀態,如果突然旋轉屏幕,由於系統配置發生了改變,在默認情況下,Activity就會被銷燬並重新創建(當然我們也可以組織系統重新創建,具體就在Mainfest中申明android:Configchanges=屬性即可)。

    異常情況下的調用流程:

    1. 調用onSaveInstance保存當前Activity狀態。注意,它與onPause方法沒有先後之分。
    2. 調用onStop方法做後續處理。
    3. 調用onDestroy方法銷燬當前活動。
    4. 重新onCreate該活動。
    5. 調用onStart方法之後,再調用onRestoreInstance方法加載保存的數據。
    6. 接下來就與正常的一樣了,調用onResume,然後運行。

    我們來看一下生命週期異常運行的系統日誌:

03-23 00:19:23.480 26457-26457/com.example.david.lifecircle E/TAG: onCreate() is invoked!
03-23 00:19:23.481 26457-26457/com.example.david.lifecircle E/TAG: onStart() is invoked!
03-23 00:19:23.481 26457-26457/com.example.david.lifecircle E/TAG: onResume() is invoked!
03-23 00:19:51.323 26457-26457/com.example.david.lifecircle E/TAG: onPause() is invoked!
03-23 00:19:51.324 26457-26457/com.example.david.lifecircle E/TAG: onSaveInstanceState() is invoked!  Save Text = Save Data
03-23 00:19:51.478 26457-26457/com.example.david.lifecircle E/TAG: onCreate() is invoked!
03-23 00:19:51.488 26457-26457/com.example.david.lifecircle E/TAG: onStart() is invoked!
03-23 00:19:51.490 26457-26457/com.example.david.lifecircle E/TAG: onRestoreInstanceState() is invoked!  Recover Text = Save Data
03-23 00:19:51.490 26457-26457/com.example.david.lifecircle E/TAG: onResume() is invoked!

  • 情況2:資源內存不足導致低優先級的Activity被殺死

    這種情況不好模擬,但是其數據存儲和恢復過程和情況1完全一致,這裏簡單的描述一下Activity的優先級情況。Activity的優先級從高到低可以大致分爲一下三種:

    (1)前臺Activity——正在和用戶交互的Activity,優先級最高。
    (2)可見但非前臺Activity——比如Activity中彈出了一個對話框,導致Activity可見但無法和用戶直接交互。
    (3)後臺Activity——已經被暫停或者停止的Activity,優先級最底。

    當系統內存不足的時候,系統就會按照上述優先級從低到高來殺死目標Activity。並在後續通過onSaveInstance和onRestoreInstance來存儲和恢復數據。
    特別提醒的是:如果一個進程中沒有四大組件(Activity、Service、ContentProvider、BroadCastReceiver)。那麼這個進程就會很快被殺死,因此一些後臺工作不適合脫離四大組件而獨立運行在後臺中,否則很容易被殺死。一般是將後臺工作放入Service中從而保證進程有一定的優先級,這樣纔不會被系統輕易殺死。


三、生命週期的使用

1.常見的生命週期有關問題:

  1. onStart和onResume、onPause和onStop從描述上看來差不多,但是他們爲什麼會分開呢?有什麼不同?
  2. 兩個Activity A和B,從A中啓動B,那麼B的onResume與A的onPause哪個會先執行呢?
  3. onSaveInstance與onRestoreInstance是任何情況下都可以使用的嘛?所有的保存數據和恢復的操作都可以在這對方法中執行?
  4. 如上面所說,如何使得在系統配置放生改變後,Activity不被重新創建呢?

2.解析

1、 onStart和onResume、onPause和onStop這兩對看起來是差不多,而且很多時候都會同時調用onPause、onStop,然後回到onStart、onResume。但是在一些比較特殊的情況下就不一樣了。我們舉兩種情況,
第一種:前臺彈出了一個Dialog,那麼這個Dialog的作用只是提醒用戶或者讓用戶輸入一個信息等就完畢了,這是一個比較輕量級的任務;
第二種:重新啓動另一個Activity界面,轉到另一個模塊。這時新啓動的Activity就不是一個臨時或者輕量級的任務了。
這兩種情況,第一種一般很快就會返回當前Activity,不會太耗時;第二種可能會很久,在這段時間內系統可能需要啓動其他的應用,那麼就會產生內存緊張的問題。所以,我認爲是要區分這兩種情況,纔會加入這兩對方法。在第一種情況下,可以在onPause中做一些較輕微的數據存儲,因爲一般很快就會回到當前Activity;第二種情況下,適合在onStop中做一些較重量級的存儲。除此之外,我想不到其他的使用了。

2、這個問題可以從源碼中得到解答。不過源碼太複雜,涉及底層太多(AMS、Binder、ActivityStack、ActivityThread等)。不過可以直接調用生命週期,輸出系統日誌來得到解答。從下面的日誌我們可以看出,的確是要等到A活動的onPause方法之後B才能執行(這裏onCreate沒有輸出日誌):

03-23 01:02:31.339 32382-32382/com.example.david.lifecircle E/MainActivity: onCreate() is invoked!
03-23 01:02:31.341 32382-32382/com.example.david.lifecircle E/MainActivity: onStart() is invoked!
03-23 01:02:31.341 32382-32382/com.example.david.lifecircle E/MainActivity: onResume() is invoked!
03-23 01:04:04.005 32382-32382/com.example.david.lifecircle E/MainActivity: onPause() is invoked!
03-23 01:04:04.047 32382-32382/com.example.david.lifecircle E/SecondActivity: onStart() is invoked!
03-23 01:04:04.047 32382-32382/com.example.david.lifecircle E/SecondActivity: onResume() is invoked!

3、 onSaveInstance和onRestoreInstance是隻有Activity異常銷燬的時候纔會調用的,所以這裏一般執行的是Activity異常銷燬時需要保存和恢復的數據;onSaveInstance和onRestoreInstance方法還可以判斷Activity是否被重建,但正常情況下是不會調用的。所以正常情況下,還是應該在onPause和onStop方法中保存數據。

4、上面提到,我們可以在AndroidMainfest.xml裏,對< activity />增加一個android:configChanges屬性,來指定在哪些配置改變的情況下Activity不需要重建。如下所示:

android:configChanges="orientation|screenSize"//界面方向以及大小的改變不需要重建
我們在AndroidMainfest.xml做如下申明:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.david.lifecircle">

    <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"
            android:configChanges="orientation|screenSize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".SecondActivity"></activity>
    </application>

</manifest>

MainActivity中的部分代碼:

 @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        Log.e("MainActivity","onConfigurationChanged() is invoked!"+newConfig.orientation);
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.e("MainActivity","onPause() is invoked!");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.e("MainActivity","onResume() is invoked!");
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.e("MainActivity","onStart() is invoked!");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.e("MainActivity","onRestart() is invoked!");
    }

點擊屏幕旋轉,然後來看一下系統日誌輸出:

03-23 01:14:11.357 10361-10361/com.example.david.lifecircle E/MainActivity: onCreate() is invoked!
03-23 01:14:11.359 10361-10361/com.example.david.lifecircle E/MainActivity: onStart() is invoked!
03-23 01:14:11.359 10361-10361/com.example.david.lifecircle E/MainActivity: onResume() is invoked!
03-23 01:14:28.140 10361-10361/com.example.david.lifecircle E/MainActivity: onConfigurationChanged() is invoked!2
03-23 01:14:38.294 10361-10361/com.example.david.lifecircle E/MainActivity: onConfigurationChanged() is invoked!1
03-23 01:14:47.531 10361-10361/com.example.david.lifecircle E/MainActivity: onConfigurationChanged() is invoked!2

我們發現,屏幕旋轉之後,並沒有重新調用生命週期,說明活動並沒有被重建。configChanges屬性還有許多的值,如:mcc\mnc\local\touchscreen\keyboard等等。

最後用一個實際的例子來說明Activity的各個生命週期。假設有一個程序由2個Activity A和B組成,A是這個程序的啓動界面。當用戶啓動程序時,Process和默認的Task分別被創建,接着A被壓入到當前的Task中,依次執行了 onCreate, onStart, onResume事件被呈現給了用戶;此時用戶選擇A中的某個功能開啓界面B,界面B被壓入當前Task遮蓋住了A,A的onPause事件執行,B的 onCreate, onStart, onResume事件執行,呈現了界面B給用戶;用戶在界面B操作完成後,使用Back鍵回到界面A,界面B不再可見,界面B的onPause, onStop, onDestroy執行,A的onResume事件被執行,呈現界面A給用戶。此時突然來電,界面A的onPause事件被執行,電話接聽界面被呈現給用 戶,用戶接聽完電話後,又按了Home鍵回到桌面,打開另一個程序“聯繫人”,添加了聯繫人信息又做了一些其他的操作,此時界面A不再可見,其 onStop事件被執行,但並沒有被銷燬。此後用戶重新從菜單中點擊了我們的程序,由於A和其所在的進程和Task並沒有被銷燬,A的onRestart 和onStart事件被執行,接着A的onResume事件被執行,A又被呈現給了用戶。用戶這次使用完後,按Back鍵返回到桌面,A的 onPause, onStop被執行,隨後A的onDestroy被執行,由於當前Task中已經沒有任何Activity,A所在的Process的重要程度被降到很 低,很快A所在的Process被系統結束
常見的例子

情形一、一個單獨的Activity的正常的生命過程是這樣的:onCreate->onStart->onPause->onStop->onDestroy。例如:運行一個Activity,進行了一些簡單操作(不涉及頁面的跳轉等),然後按返回鍵結束。

情形二、有兩個Activity(a和b),一開始顯示a,然後由a啓動b,然後在由b回到a,這時候a的生命過程應該是怎麼樣的呢(a被b完全遮蓋)?

a經歷的過程爲onCreate->onStart->onResume->onPause->onStop->onRestart->onStart->onResume。這個過程說明了圖中,如果Activity完全被其他界面遮擋時,進入後臺,並沒有完全銷燬,而是停留在onStop狀態,當再次進入a時,onRestart->onStart->onResume,又重新恢復。

情形三、基本情形同二一樣,不過此時a被b部分遮蓋(比如給b添加個對話框主題 android:theme=”@android:style/Theme.Dialog”)

a經歷的過程是:onCreate->onStart->onResume->onPause->onResume

所以當Activity被部分遮擋時,Activity進入onPause,並沒有進入onStop,從Activity2返回後,執行了onResume

情形四、 打開程序,啓動a,點擊a,啓動AlertDialog,按返回鍵從AlertDialog返回。

a經歷的過程是:onCreate->onStart->onResume

當啓動和退出Dialog時,Activity的狀態始終未變,,因爲Dialog實際上時一個View,它是屬於某一個Activity的,因此如果Dialog顯示在當前Activity之前時不會影響到Activity的生命週期的。但是如果是其他Activity的Dialog彈出那麼就會觸發onPause()方法的執行。

發佈了54 篇原創文章 · 獲贊 44 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章