啟動(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.簡化的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) (Created與Started)都是短暫的瞬態(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. 上圖顯示了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)悉盆。
當(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)資源鏈接网梢。
上圖顯示:當(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() 方法中哮缺。
當(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包斑。