管理Activity的生命周期

啟動(dòng)與銷毀Activity

不同于使用 main() 方法啟動(dòng)應(yīng)用的其他編程范例,Android 系統(tǒng)會(huì)通過調(diào)用對(duì)應(yīng)于其生命周期中特定階段的特定回調(diào)方法在 Activity 實(shí)例中啟動(dòng)代碼浩蓉。 有一系列可啟動(dòng)Activity的回調(diào)方法,以及一系列可分解Activity的回調(diào)方法。

本課程概述了最重要的生命周期方法孝赫,并向您展示如何處理創(chuàng)建Activity新實(shí)例的第一個(gè)生命周期回調(diào)。

了解生命周期回調(diào)

在Activity的生命周期中红符,系統(tǒng)會(huì)按類似于階梯金字塔的順序調(diào)用一組核心的生命周期方法青柄。也就是說伐债,Activity生命周期的每個(gè)階段就是金字塔上的一階。 當(dāng)系統(tǒng)創(chuàng)建新Activity實(shí)例時(shí)致开,每個(gè)回調(diào)方法會(huì)將Activity狀態(tài)向頂端移動(dòng)一階峰锁。金字塔的頂端是Activity在前臺(tái)運(yùn)行并且用戶可以與其交互的時(shí)間點(diǎn)。

當(dāng)用戶開始離開Activity時(shí)双戳,系統(tǒng)會(huì)調(diào)用其他方法在金字塔中將Activity狀態(tài)下移虹蒋,從而銷毀Activity。在有些情況下飒货,Activity將只在金字塔中部分下移并等待(比如魄衅,當(dāng)用戶切換到其他應(yīng)用時(shí)),Activity可從該點(diǎn)開始移回頂端(如果用戶返回到該Activity)塘辅,并在用戶停止的位置繼續(xù)晃虫。

1.png

圖 1.簡化的Activity生命周期圖示,以階梯金字塔表示扣墩。此圖示顯示哲银,對(duì)于用于將Activity朝頂端的“繼續(xù)”狀態(tài)移動(dòng)一階的每個(gè)回調(diào),有一種將Activity下移一階的回調(diào)方法呻惕。Activity還可以從“暫途T穑”和“停止”狀態(tài)回到繼續(xù)狀態(tài)。
根據(jù)Activity的復(fù)雜程度亚脆,您可能不需要實(shí)現(xiàn)所有生命周期方法做院。但是,了解每個(gè)方法并實(shí)現(xiàn)確保您的應(yīng)用按照用戶期望的方式運(yùn)行的方法非常重要濒持。正確實(shí)現(xiàn)您的Activity生命周期方法可確保您的應(yīng)用按照以下幾種方式良好運(yùn)行键耕,包括:

  • 如果用戶在使用您的應(yīng)用時(shí)接聽來電或切換到另一個(gè)應(yīng)用,它不會(huì)崩潰弥喉。
  • 在用戶未主動(dòng)使用它時(shí)不會(huì)消耗寶貴的系統(tǒng)資源郁竟。
  • 如果用戶離開您的應(yīng)用并稍后返回玛迄,不會(huì)丟失用戶的進(jìn)度由境。
  • 當(dāng)屏幕在橫向和縱向之間旋轉(zhuǎn)時(shí),不會(huì)崩潰或丟失用戶的進(jìn)度蓖议。

正如您將要在以下課程中要學(xué)習(xí)的虏杰,有Activity會(huì)在圖 1 所示不同狀態(tài)之間過渡的幾種情況。但是勒虾,這些狀態(tài)中只有三種可以是靜態(tài)纺阔。 也就是說,Activity只能在三種狀態(tài)之一下存在很長時(shí)間修然。

  • Resumed:在這種狀態(tài)下笛钝,Activity處于前臺(tái)质况,且用戶可以與其交互。(有時(shí)也稱為“運(yùn)行”狀態(tài)玻靡。)
  • Paused:在這種狀態(tài)下结榄,Activity被在前臺(tái)中處于半透明狀態(tài)或者未覆蓋整個(gè)屏幕的另一個(gè)Activity—部分阻擋。暫停的Activity不會(huì)接收用戶輸入并且無法執(zhí)行任何代碼囤捻。
  • Stopped:在這種狀態(tài)下臼朗,Activity被完全隱藏并且對(duì)用戶不可見;它被視為處于后臺(tái)蝎土。停止時(shí)视哑,Activity實(shí)例及其諸如成員變量等所有狀態(tài)信息將保留,但它無法執(zhí)行任何代碼誊涯。

其他狀態(tài)(“創(chuàng)建”和“開始”)是瞬態(tài)挡毅,
其它狀態(tài) (CreatedStarted)都是短暫的瞬態(tài),系統(tǒng)會(huì)通過調(diào)用下一個(gè)生命周期回調(diào)方法從這些狀態(tài)快速移到下一個(gè)狀態(tài)醋拧。 也就是說慷嗜,在系統(tǒng)調(diào)用 onCreate() 之后,它會(huì)快速調(diào)用 onStart()丹壕,緊接著快速調(diào)用 onResume()庆械。

指定程序首次啟動(dòng)的Activity

當(dāng)用戶從主界面點(diǎn)擊程序圖標(biāo)時(shí),系統(tǒng)會(huì)調(diào)用app中被聲明為"launcher" (or "main") activity中的onCreate()方法菌赖。這個(gè)Activity被用來當(dāng)作程序的主要進(jìn)入點(diǎn)缭乘。
我們可以在AndroidManifest.xml中定義作為主activity的activity。
這個(gè)main activity必須在manifest使用包括 MAIN
action 與 LAUNCHER
category 的<intent-filter>
標(biāo)簽來聲明琉用。例如:

<activity android:name=".MainActivity" android:label="@string/app_name">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

tips:當(dāng)你使用Android SDK工具來創(chuàng)建Android工程時(shí)堕绩,工程中就包含了一個(gè)默認(rèn)的聲明有這個(gè)filter的activity類。
如果程序中沒有聲明了MAIN action 或者LAUNCHER category的activity邑时,那么在設(shè)備的主界面列表里面不會(huì)呈現(xiàn)app圖標(biāo)奴紧。

創(chuàng)建一個(gè)新的實(shí)例

大多數(shù)app包括多個(gè)activity,使用戶可以執(zhí)行不同的動(dòng)作晶丘。不論這個(gè)activity是當(dāng)用戶點(diǎn)擊應(yīng)用圖標(biāo)創(chuàng)建的main activtiy還是為了響應(yīng)用戶行為而創(chuàng)建的其他activity黍氮,系統(tǒng)都會(huì)調(diào)用新activity實(shí)例中的onCreate()方法。

我們必須實(shí)現(xiàn)onCreate()方法來執(zhí)行程序啟動(dòng)所需要的基本邏輯浅浮。例如可以在onCreate()方法中定義UI以及實(shí)例化類成員變量沫浆。

例如:下面的onCreate()方法演示了為了建立一個(gè)activity所需要的一些基礎(chǔ)操作。如聲明UI元素滚秩,定義成員變量专执,配置UI等。(onCreate里面盡量少做事情郁油,避免程序啟動(dòng)太久都看不到界面)

TextView mTextView; // Member variable for text view in the layout

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Set the user interface layout for this Activity
    // The layout file is defined in the project res/layout/main_activity.xml file
    setContentView(R.layout.main_activity);

    // Initialize member TextView so we can manipulate it later
    mTextView = (TextView) findViewById(R.id.text_message);

    // Make sure we're running on Honeycomb or higher to use ActionBar APIs
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        // For the main activity, make sure the app icon in the action bar
        // does not behave as a button
        ActionBar actionBar = getActionBar();
        actionBar.setHomeButtonEnabled(false);
    }
}

Caution:用SDK_INT來避免舊的系統(tǒng)調(diào)用了只在Android 2.0(API level 5)或者更新的系統(tǒng)可用的方法(上述if條件中的代碼)本股。舊的系統(tǒng)調(diào)用了這些方法會(huì)拋出一個(gè)運(yùn)行時(shí)異常攀痊。

一旦onCreate 操作完成,系統(tǒng)會(huì)迅速調(diào)用onStart() 與onResume()方法拄显。我們的activity不會(huì)在Created或者Started狀態(tài)停留蚕苇。技術(shù)上來說, activity在onStart()被調(diào)用后開始被用戶可見,但是 onResume()會(huì)迅速被執(zhí)行使得activity停留在Resumed狀態(tài)凿叠,直到一些因素發(fā)生變化才會(huì)改變這個(gè)狀態(tài)涩笤。例如接收到一個(gè)來電,用戶切換到另外一個(gè)activity盒件,或者是設(shè)備屏幕關(guān)閉蹬碧。
在后面的課程中,我們將看到其他方法是如何使用的炒刁,onStart() 與 onResume()在用戶從Paused或Stopped狀態(tài)中恢復(fù)的時(shí)候非常有用恩沽。

2.png

圖2. 上圖顯示了onCreate(), onStart() 和 onResume()是如何執(zhí)行的。當(dāng)這三個(gè)順序執(zhí)行的回調(diào)函數(shù)完成后翔始,activity會(huì)到達(dá)Resumed狀態(tài)罗心。

銷毀Activity

activity的第一個(gè)生命周期回調(diào)函數(shù)是 onCreate(),它最后一個(gè)回調(diào)是onDestroy().當(dāng)收到需要將該activity徹底移除的信號(hào)時(shí),系統(tǒng)會(huì)調(diào)用這個(gè)方法城瞎。
大多數(shù) app并不需要實(shí)現(xiàn)這個(gè)方法渤闷,因?yàn)榫植款惖膔eferences會(huì)隨著activity的銷毀而銷毀,并且我們的activity應(yīng)該在onPause()與onStop()中執(zhí)行清除activity資源的操作脖镀。然而飒箭,如果activity含有在onCreate調(diào)用時(shí)創(chuàng)建的后臺(tái)線程,或者是其他有可能導(dǎo)致內(nèi)存泄漏的資源蜒灰,則應(yīng)該在OnDestroy()時(shí)進(jìn)行資源清理弦蹂,殺死后臺(tái)線程。

@Override
public void onDestroy() {
    super.onDestroy();  // Always call the superclass

    // Stop method tracing that the activity started during onCreate()
    android.os.Debug.stopMethodTracing();
}

tips: 除非程序在onCreate()方法里面就調(diào)用了finish()方法强窖,系統(tǒng)通常是在執(zhí)行了onPause()與onStop() 之后再調(diào)用onDestroy() 凸椿。在某些情況下,例如我們的activity只是做了一個(gè)臨時(shí)的邏輯跳轉(zhuǎn)的功能翅溺,它只是用來決定跳轉(zhuǎn)到哪一個(gè)activity脑漫,這樣的話,需要在onCreate里面調(diào)用finish方法未巫,這樣系統(tǒng)會(huì)直接調(diào)用onDestory窿撬,跳過生命周期中的其他方法启昧。

暫停與恢復(fù)Activity

在正常使用app時(shí)叙凡,前端的activity有時(shí)會(huì)被其他可見的組件阻塞(obstructed),從而導(dǎo)致當(dāng)前的activity進(jìn)入Pause狀態(tài)密末。例如握爷,當(dāng)打開一個(gè)半透明的activity時(shí)(例如以對(duì)話框的形式)跛璧,之前的activity會(huì)被暫停。 只要之前的activity仍然被部分可見新啼,這個(gè)activity就會(huì)一直處于Paused狀態(tài)追城。

然而,一旦之前的activity被完全阻塞并不可見時(shí)燥撞,則其會(huì)進(jìn)入Stop狀態(tài)(將在下一小節(jié)討論)座柱。
activity一旦進(jìn)入paused狀態(tài),系統(tǒng)就會(huì)調(diào)用activity中的onPause()方法, 該方法中可以停止不應(yīng)該在暫停過程中執(zhí)行的操作物舒,如暫停視頻播放色洞;或者保存那些有可能需要長期保存的信息。如果用戶從暫停狀態(tài)回到當(dāng)前activity冠胯,系統(tǒng)應(yīng)該恢復(fù)那些數(shù)據(jù)并執(zhí)行onResume()方法火诸。

Note: 當(dāng)我們的activity收到調(diào)用onPause()的信號(hào)時(shí),那可能意味者activity將被暫停一段時(shí)間荠察,并且用戶很可能回到我們的activity置蜀。然而,那也是用戶要離開我們的activtiy的第一個(gè)信號(hào)悉盆。

3.png

當(dāng)一個(gè)半透明的activity阻塞activity時(shí)盯荤,系統(tǒng)會(huì)調(diào)用onPause()方法并且這個(gè)activity會(huì)停留在Paused 狀態(tài)(1). 如果用戶在這個(gè)activity還是在Paused 狀態(tài)時(shí)回到這個(gè)activity,系統(tǒng)則會(huì)調(diào)用它的onResume() (2).

暫停Activity

當(dāng)系統(tǒng)調(diào)用activity中的onPause()焕盟,從技術(shù)上講廷雅,意味著activity仍然處于部分可見的狀態(tài).但更多時(shí)候意味著用戶正在離開這個(gè)activity,并馬上會(huì)進(jìn)入Stopped state. 通常應(yīng)該在onPause()回調(diào)方法里面做以下事情:

  • 停止動(dòng)畫或者是其他正在運(yùn)行的操作京髓,那些都會(huì)導(dǎo)致CPU的浪費(fèi).
  • 提交在用戶離開時(shí)期待保存的內(nèi)容(例如郵件草稿).
  • 釋放系統(tǒng)資源航缀,例如broadcast receivers, sensors (比如GPS), 或者是其他任何會(huì)影響到電量的資源。

例如, 如果程序使用Camera,onPause()會(huì)是一個(gè)比較好的地方去做那些釋放資源的操作堰怨。

@Override
public void onPause() {
    super.onPause();  // Always call the superclass method first

    // Release the Camera because we don't need it when paused
    // and other activities might need to use it.
    if (mCamera != null) {
        mCamera.release()
        mCamera = null;
    }
}

通常芥玉,不應(yīng)該使用onPause()來保存用戶改變的數(shù)據(jù) (例如填入表格中的個(gè)人信息) 到永久存儲(chǔ)(File或者DB)上。僅僅當(dāng)確認(rèn)用戶期待那些改變能夠被自動(dòng)保存的時(shí)候(例如正在撰寫郵件草稿)备图,才把那些數(shù)據(jù)存到永久存儲(chǔ) 灿巧。但是,我們應(yīng)該避免在onPause()時(shí)執(zhí)行CPU-intensive 的工作揽涮,例如寫數(shù)據(jù)到DB抠藕,因?yàn)樗鼤?huì)導(dǎo)致切換到下一個(gè)activity變得緩慢(應(yīng)該把那些heavy-load的工作放到onStop()去做)。

如果activity實(shí)際上是要被Stop蒋困,那么我們應(yīng)該為了切換的順暢而減少在OnPause()方法里面的工作量盾似。
Note:當(dāng)activity處于暫停狀態(tài),Activity實(shí)例是駐留在內(nèi)存中的雪标,并且在activity 恢復(fù)的時(shí)候重新調(diào)用零院。我們不需要在恢復(fù)到Resumed狀態(tài)的一系列回調(diào)方法中重新初始化組件溉跃。

恢復(fù)activity

當(dāng)用戶從Paused狀態(tài)恢復(fù)activity時(shí),系統(tǒng)會(huì)調(diào)用onResume()方法告抄。

請(qǐng)注意撰茎,系統(tǒng)每次調(diào)用這個(gè)方法時(shí),activity都處于前臺(tái)打洼,包括第一次創(chuàng)建的時(shí)候龄糊。所以,應(yīng)該實(shí)現(xiàn)onResume()來初始化那些在onPause方法里面釋放掉的組件募疮,并執(zhí)行那些activity每次進(jìn)入Resumed state都需要的初始化動(dòng)作 (例如開始動(dòng)畫與初始化那些只有在獲取用戶焦點(diǎn)時(shí)才需要的組件)

下面的onResume()的例子是與上面的onPause()例子相對(duì)應(yīng)的绎签。

@Override
public void onResume() {
    super.onResume();  // Always call the superclass method first

    // Get the Camera instance as the activity achieves full user focus
    if (mCamera == null) {
        initializeCamera(); // Local method to handle camera init
    }
}

停止與重啟Activity

恰當(dāng)?shù)耐V古c重啟我們的activity是很重要的,在activity生命周期中酝锅,他們能確保用戶感知到程序的存在并不會(huì)丟失他們的進(jìn)度诡必。在下面一些關(guān)鍵的場(chǎng)景中會(huì)涉及到停止與重啟:

  • 用戶打開最近使用app的菜單并從我們的app切換到另外一個(gè)app,這個(gè)時(shí)候我們的app是被停止的搔扁。如果用戶通過手機(jī)主界面的啟動(dòng)程序圖標(biāo)或者最近使用程序的窗口回到我們的app爸舒,那么我們的activity會(huì)重啟。
  • 用戶在我們的app里面執(zhí)行啟動(dòng)一個(gè)新activity的操作稿蹲,當(dāng)前activity會(huì)在第二個(gè)activity被創(chuàng)建后stop扭勉。如果用戶點(diǎn)擊back按鈕,第一個(gè)activtiy會(huì)被重啟苛聘。
  • 用戶在使用我們的app時(shí)接收到一個(gè)來電通話.

Activity類提供了onStop()onRestart()方法來允許在activity停止與重啟時(shí)進(jìn)行調(diào)用涂炎。不同于暫停狀態(tài)的部分阻塞UI,停止?fàn)顟B(tài)是UI不再可見并且用戶的焦點(diǎn)轉(zhuǎn)移到另一個(gè)activity中.
Note: 因?yàn)橄到y(tǒng)在activity停止時(shí)會(huì)在內(nèi)存中保存Activity的實(shí)例设哗,所以有時(shí)不需要實(shí)現(xiàn)onStop(),onRestart()甚至是onStart()方法. 因?yàn)榇蠖鄶?shù)的activity相對(duì)比較簡單唱捣,activity會(huì)自己停止與重啟,我們只需要使用onPause()來停止正在運(yùn)行的動(dòng)作并斷開系統(tǒng)資源鏈接网梢。

4.png

上圖顯示:當(dāng)用戶離開我們的activity時(shí)震缭,系統(tǒng)會(huì)調(diào)用onStop()來停止activity (1). 這個(gè)時(shí)候如果用戶返回,系統(tǒng)會(huì)調(diào)用onRestart()(2), 之后會(huì)迅速調(diào)用onStart()(3)與onResume()(4). 請(qǐng)注意:無論什么原因?qū)е耡ctivity停止战虏,系統(tǒng)總是會(huì)在onStop()之前調(diào)用onPause()方法拣宰。

停止activity

當(dāng)activity調(diào)用onStop()方法, activity不再可見,并且應(yīng)該釋放那些不再需要的所有資源烦感。一旦activity停止了巡社,系統(tǒng)會(huì)在需要內(nèi)存空間時(shí)摧毀它的實(shí)例(和棧結(jié)構(gòu)有關(guān),通常back操作會(huì)導(dǎo)致前一個(gè)activity被銷毀)手趣。極端情況下晌该,系統(tǒng)會(huì)直接殺死我們的app進(jìn)程,并不執(zhí)行activity的onDestroy()回調(diào)方法, 因此我們需要使用onStop()來釋放資源,從而避免內(nèi)存泄漏气笙。(這點(diǎn)需要注意)
盡管onPause()方法是在onStop()之前調(diào)用,我們應(yīng)該使用onStop()來執(zhí)行那些CPU intensive的shut-down操作怯晕,例如往數(shù)據(jù)庫寫信息潜圃。
例如,下面是一個(gè)在onStop()的方法里面保存筆記草稿到persistent storage的示例:

@Override
protected void onStop() {
    super.onStop();  // Always call the superclass method first

    // Save the note's current draft, because the activity is stopping
    // and we want to be sure the current note progress isn't lost.
    ContentValues values = new ContentValues();
    values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText());
    values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle());

    getContentResolver().update(
            mUri,    // The URI for the note to update.
            values,  // The map of column names and new values to apply to them.
            null,    // No SELECT criteria are used.
            null     // No WHERE columns are used.
            );
}

activity已經(jīng)停止后舟茶,Activity對(duì)象會(huì)保存在內(nèi)存中谭期,并在activity resume時(shí)被重新調(diào)用。我們不需要在恢復(fù)到Resumed state狀態(tài)前重新初始化那些被保存在內(nèi)存中的組件吧凉。系統(tǒng)同樣保存了每一個(gè)在布局中的視圖的當(dāng)前狀態(tài)隧出,如果用戶在EditText組件中輸入了text,它會(huì)被保存阀捅,因此不需要保存與恢復(fù)它胀瞪。
Note: 即使系統(tǒng)會(huì)在activity stop時(shí)停止這個(gè)activity,它仍然會(huì)保存View對(duì)象的狀態(tài)(比如EditText中的文字) 到一個(gè)Bundle中饲鄙,并且在用戶返回這個(gè)activity時(shí)恢復(fù)它們(下一小節(jié)會(huì)介紹在activity銷毀與重新建立時(shí)如何使用Bundle來保存其他數(shù)據(jù)的狀態(tài)).

啟動(dòng)與重啟activity

當(dāng)activity從Stopped狀態(tài)回到前臺(tái)時(shí)凄诞,它會(huì)調(diào)用onRestart().系統(tǒng)再調(diào)用onStart()方法,onStart()方法會(huì)在每次activity可見時(shí)都會(huì)被調(diào)用忍级。onRestart()方法則是只在activity從stopped狀態(tài)恢復(fù)時(shí)才會(huì)被調(diào)用帆谍,因此我們可以使用它來執(zhí)行一些特殊的恢復(fù)(restoration)工作,請(qǐng)注意之前是被stopped而不是destrory轴咱。

使用onRestart()來恢復(fù)activity狀態(tài)是不太常見的汛蝙,因此對(duì)于這個(gè)方法如何使用沒有任何的guidelines。然而朴肺,因?yàn)閛nStop()方法應(yīng)該做清除所有activity資源的操作窖剑,我們需要在重啟activtiy時(shí)重新實(shí)例化那些被清除的資源,同樣, 我們也需要在activity第一次創(chuàng)建時(shí)實(shí)例化那些資源戈稿。介于上面的原因苛吱,應(yīng)該使用onStart()作為onStop()所對(duì)應(yīng)方法。因?yàn)橄到y(tǒng)會(huì)在創(chuàng)建activity與從停止?fàn)顟B(tài)重啟activity時(shí)都會(huì)調(diào)用onStart()器瘪。也就是說翠储,我們?cè)趏nStop里面做了哪些清除的操作,就該在onStart里面重新把那些清除掉的資源重新創(chuàng)建出來橡疼。

例如:因?yàn)橛脩艉芸赡茉诨氐竭@個(gè)activity之前已經(jīng)過了很長一段時(shí)間援所,所以onStart()方法是一個(gè)比較好的地方來驗(yàn)證某些必須的系統(tǒng)特性是否可用。

@Override
protected void onStart() {
    super.onStart();  // Always call the superclass method first

    // The activity is either being restarted or started for the first time
    // so this is where we should make sure that GPS is enabled
    LocationManager locationManager =
            (LocationManager) getSystemService(Context.LOCATION_SERVICE);
    boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);

    if (!gpsEnabled) {
        // Create a dialog here that requests the user to enable GPS, and use an intent
        // with the android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS action
        // to take the user to the Settings screen to enable GPS when they click "OK"
    }
}

@Override
protected void onRestart() {
    super.onRestart();  // Always call the superclass method first

    // Activity being restarted from stopped state
}

當(dāng)系統(tǒng)Destory我們的activity欣除,它會(huì)為activity調(diào)用onDestroy()方法住拭。因?yàn)槲覀儠?huì)在onStop方法里面做釋放資源的操作,那么onDestory方法則是我們最后去清除那些可能導(dǎo)致內(nèi)存泄漏的地方。因此需要確保那些線程都被destroyed并且所有的操作都被停止滔岳。

重新創(chuàng)建Activity

有幾個(gè)場(chǎng)景中杠娱,Activity是由于正常的程序行為而被Destory的。例如當(dāng)用戶點(diǎn)擊返回按鈕或者是Activity通過調(diào)用finish()來發(fā)出停止信號(hào)谱煤。系統(tǒng)也有可能會(huì)在Activity處于stop狀態(tài)且長時(shí)間不被使用摊求,或者是在前臺(tái)activity需要更多系統(tǒng)資源的時(shí)關(guān)閉后臺(tái)進(jìn)程,以圖獲取更多的內(nèi)存刘离。

當(dāng)Activity是因?yàn)橛脩酎c(diǎn)擊Back按鈕或者是activity通過調(diào)用finish()結(jié)束自己時(shí)室叉,系統(tǒng)就丟失了對(duì)Activity實(shí)例的引用,因?yàn)檫@一行為意味著不再需要這個(gè)activity了硫惕。然而茧痕,如果因?yàn)橄到y(tǒng)資源緊張而導(dǎo)致Activity的Destory, 系統(tǒng)會(huì)在用戶回到這個(gè)Activity時(shí)有這個(gè)Activity存在過的記錄恼除,系統(tǒng)會(huì)使用那些保存的記錄數(shù)據(jù)(描述了當(dāng)Activity被Destory時(shí)的狀態(tài))來重新創(chuàng)建一個(gè)新的Activity實(shí)例踪旷。那些被系統(tǒng)用來恢復(fù)之前狀態(tài)而保存的數(shù)據(jù)被叫做 "instance state" ,它是一些存放在Bundle對(duì)象中的key-value pairs豁辉。(請(qǐng)注意這里的描述埃脏,這對(duì)理解onSaveInstanceState執(zhí)行的時(shí)刻很重要)
tips: 你的Activity會(huì)在每次旋轉(zhuǎn)屏幕時(shí)被destroyed與recreated。當(dāng)屏幕改變方向時(shí)秋忙,系統(tǒng)會(huì)Destory與Recreate前臺(tái)的activity彩掐,因?yàn)槠聊慌渲帽桓淖儯愕腁ctivity可能需要加載另一些替代的資源(例如layout).

默認(rèn)情況下, 系統(tǒng)使用 Bundle 實(shí)例來保存每一個(gè)View(視圖)對(duì)象中的信息(例如輸入EditText 中的文本內(nèi)容)灰追。因此堵幽,如果Activity被destroyed與recreated, 則layout的狀態(tài)信息會(huì)自動(dòng)恢復(fù)到之前的狀態(tài)。然而弹澎,activity也許存在更多你想要恢復(fù)的狀態(tài)信息朴下,例如記錄用戶Progress的成員變量(member variables)。
Note: 為了使Android系統(tǒng)能夠恢復(fù)Activity中的View的狀態(tài)苦蒿,每個(gè)View都必須有一個(gè)唯一ID殴胧,由android:id定義。

為了可以保存額外更多的數(shù)據(jù)到saved instance state佩迟。在Activity的生命周期里面存在一個(gè)額外的回調(diào)函數(shù)团滥,你必須重寫這個(gè)函數(shù)。該回調(diào)函數(shù)并沒有在前面課程的圖片示例中顯示报强。這個(gè)方法是onSaveInstanceState() 灸姊,當(dāng)用戶離開Activity時(shí),系統(tǒng)會(huì)調(diào)用它秉溉。當(dāng)系統(tǒng)調(diào)用這個(gè)函數(shù)時(shí)力惯,系統(tǒng)會(huì)在Activity被異常Destory時(shí)傳遞 Bundle 對(duì)象碗誉,這樣我們就可以增加額外的信息到Bundle中并保存到系統(tǒng)中。若系統(tǒng)在Activity被Destory之后想重新創(chuàng)建這個(gè)Activity實(shí)例時(shí)父晶,之前的Bundle對(duì)象會(huì)(系統(tǒng))被傳遞到你我們activity的onRestoreInstanceState()方法與 onCreate() 方法中哮缺。

5.png

當(dāng)系統(tǒng)開始停止Activity時(shí),只有在Activity實(shí)例會(huì)需要重新創(chuàng)建的情況下才會(huì)調(diào)用到onSaveInstanceState() (1) 甲喝,在這個(gè)方法里面可以指定額外的狀態(tài)數(shù)據(jù)到Bunde中尝苇。如果這個(gè)Activity被destroyed然后這個(gè)實(shí)例又需要被重新創(chuàng)建時(shí),系統(tǒng)會(huì)傳遞在 (1) 中的狀態(tài)數(shù)據(jù)到 onCreate() (2) 與 onRestoreInstanceState()(3).

(通常來說俺猿,跳轉(zhuǎn)到其他的activity或者是點(diǎn)擊Home都會(huì)導(dǎo)致當(dāng)前的activity執(zhí)行onSaveInstanceState茎匠,因?yàn)檫@種情況下的activity都是有可能會(huì)被destory并且是需要保存狀態(tài)以便后續(xù)恢復(fù)使用的格仲,而從跳轉(zhuǎn)的activity點(diǎn)擊back回到前一個(gè)activity押袍,那么跳轉(zhuǎn)前的activity是執(zhí)行退棧的操作,所以這種情況下是不會(huì)執(zhí)行onSaveInstanceState的凯肋,因?yàn)檫@個(gè)activity不可能存在需要重建的操作)

保存Activity狀態(tài)

當(dāng)我們的activity開始Stop谊惭,系統(tǒng)會(huì)調(diào)用 onSaveInstanceState() ,Activity可以用鍵值對(duì)的集合來保存狀態(tài)信息侮东。這個(gè)方法會(huì)默認(rèn)保存Activity視圖的狀態(tài)信息圈盔,如在 EditText 組件中的文本或 ListView 的滑動(dòng)位置。

為了給Activity保存額外的狀態(tài)信息悄雅,你必須實(shí)現(xiàn)onSaveInstanceState() 并增加key-value pairs到 Bundle 對(duì)象中驱敲,例如:

static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
...

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save the user's current game state
    savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
    savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);

    // Always call the superclass so it can save the view hierarchy state
    super.onSaveInstanceState(savedInstanceState);
}

tips: 必須要調(diào)用 onSaveInstanceState() 方法的父類實(shí)現(xiàn),這樣默認(rèn)的父類實(shí)現(xiàn)才能保存視圖狀態(tài)的信息宽闲。

恢復(fù)Activity狀態(tài)

當(dāng)Activity從Destory中重建众眨,我們可以從系統(tǒng)傳遞的Activity的Bundle中恢復(fù)保存的狀態(tài)。 onCreate() 與 onRestoreInstanceState() 回調(diào)方法都接收到了同樣的Bundle容诬,里面包含了同樣的實(shí)例狀態(tài)信息娩梨。

由于 onCreate() 方法會(huì)在第一次創(chuàng)建新的Activity實(shí)例與重新創(chuàng)建之前被Destory的實(shí)例時(shí)都被調(diào)用,我們必須在嘗試讀取 Bundle 對(duì)象前檢測(cè)它是否為null览徒。如果它為null狈定,系統(tǒng)則是創(chuàng)建一個(gè)新的Activity實(shí)例,而不是恢復(fù)之前被Destory的Activity习蓬。

下面是一個(gè)示例:演示在onCreate方法里面恢復(fù)一些數(shù)據(jù):

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); // Always call the superclass first

    // Check whether we're recreating a previously destroyed instance
    if (savedInstanceState != null) {
        // Restore value of members from saved state
        mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
        mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
    } else {
        // Probably initialize members with default values for a new instance
    }
    ...
}

我們也可以選擇實(shí)現(xiàn) onRestoreInstanceState() 纽什,而不是在onCreate方法里面恢復(fù)數(shù)據(jù)。 onRestoreInstanceState()方法會(huì)在 onStart() 方法之后執(zhí)行. 系統(tǒng)僅僅會(huì)在存在需要恢復(fù)的狀態(tài)信息時(shí)才會(huì)調(diào)用 onRestoreInstanceState() 躲叼,因此不需要檢查 Bundle 是否為null稿湿。

public void onRestoreInstanceState(Bundle savedInstanceState) {
    // Always call the superclass so it can restore the view hierarchy
    super.onRestoreInstanceState(savedInstanceState);

    // Restore state members from saved instance
    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
    mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
}

tips: 與上面保存一樣,總是需要調(diào)用onRestoreInstanceState()方法的父類實(shí)現(xiàn)押赊,這樣默認(rèn)的父類實(shí)現(xiàn)才能保存視圖狀態(tài)的信息饺藤。更多關(guān)于運(yùn)行時(shí)狀態(tài)改變引起的recreate我們的activity包斑。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市涕俗,隨后出現(xiàn)的幾起案子罗丰,更是在濱河造成了極大的恐慌,老刑警劉巖再姑,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萌抵,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡元镀,警方通過查閱死者的電腦和手機(jī)绍填,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來栖疑,“玉大人讨永,你說我怎么就攤上這事∮龈铮” “怎么了卿闹?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長萝快。 經(jīng)常有香客問我锻霎,道長,這世上最難降的妖魔是什么揪漩? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任旋恼,我火速辦了婚禮,結(jié)果婚禮上奄容,老公的妹妹穿的比我還像新娘冰更。我一直安慰自己,他們只是感情好嫩海,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布冬殃。 她就那樣靜靜地躺著,像睡著了一般叁怪。 火紅的嫁衣襯著肌膚如雪审葬。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天奕谭,我揣著相機(jī)與錄音涣觉,去河邊找鬼。 笑死血柳,一個(gè)胖子當(dāng)著我的面吹牛官册,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播难捌,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼膝宁,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼鸦难!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起员淫,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤合蔽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后介返,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拴事,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年圣蝎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了刃宵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡徘公,死狀恐怖牲证,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情步淹,我是刑警寧澤从隆,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布诚撵,位于F島的核電站缭裆,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏寿烟。R本人自食惡果不足惜澈驼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望筛武。 院中可真熱鬧缝其,春花似錦、人聲如沸徘六。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽待锈。三九已至漠其,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間竿音,已是汗流浹背和屎。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留春瞬,地道東北人柴信。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像宽气,于是被迫代替她去往敵國和親随常。 傳聞我的和親對(duì)象是個(gè)殘疾皇子潜沦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 您的應(yīng)用中的Activity應(yīng)該做到如下需求: 1.用戶在使用應(yīng)用時(shí)接聽來電或切換到另一個(gè)應(yīng)用,它不會(huì)崩潰绪氛。 2....
    正陽Android閱讀 524評(píng)論 1 0
  • 媽媽說喜歡就好止潮。雖然未能如愿進(jìn)入心儀的高校讀研,承蒙老天厚愛钞楼,很意外的進(jìn)入和教育相關(guān)的行業(yè)喇闸。也不枉我苦讀半年教育學(xué)...
    不一樣的煙火sss閱讀 362評(píng)論 0 0
  • 不知道大家有沒有對(duì)自己小時(shí)候的記憶,總是聽家里的大人說我小時(shí)候多么多么的愛整潔询件,會(huì)說話燃乍。 在今天家人還津津樂道的一...
    Lavender錚閱讀 545評(píng)論 7 8
  • 作為一只23年來保持單身記錄的狗,不宛琅,準(zhǔn)確的來說刻蟹,24年,身邊有不少人問我嘿辟;“你為什么還不拍拖舆瘪?”好久不見的朋友喜...
    櫻花蝸牛閱讀 465評(píng)論 2 3