啟動與銷毀Activity
不同于使用 main() 方法啟動應用的其他編程范例再榄,Android 系統(tǒng)會通過調用對應于其生命周期中特定階段的特定回調方法在 Activity 實例中啟動代碼丰捷。 有一系列可啟動Activity的回調方法刻坊,以及一系列可分解Activity的回調方法。
本課程概述了最重要的生命周期方法吗跋,并向您展示如何處理創(chuàng)建Activity新實例的第一個生命周期回調丈牢。
啟動第一個Activity
當用戶從主界面點擊程序圖標時鳞青,系統(tǒng)會調用app中被聲明為"launcher" (or "main") activity中的onCreate()方法衙四。這個Activity被用來當作程序的主要進入點铃肯。
我們可以在AndroidManifest.xml中定義作為主activity的activity。
這個main activity必須在manifest使用包括 MAIN
action 與 LAUNCHER
category 的<intent-filter>
標簽來聲明传蹈。例如:
<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>
Note:當你使用Android SDK工具來創(chuàng)建Android工程時押逼,工程中就包含了一個默認的聲明有這個filter的activity類。
如果程序中沒有聲明了MAIN action 或者LAUNCHER category的activity惦界,那么在設備的主界面列表里面不會呈現(xiàn)app圖標宴胧。
生命周期回調
在Activity的生命周期中,系統(tǒng)會按類似于階梯金字塔的順序調用一組核心的生命周期方法表锻。也就是說恕齐,Activity生命周期的每個階段就是金字塔上的一階。 當系統(tǒng)創(chuàng)建新Activity實例時瞬逊,每個回調方法會將Activity狀態(tài)向頂端移動一階显歧。金字塔的頂端是Activity在前臺運行并且用戶可以與其交互的時間點。
當用戶開始離開Activity時确镊,系統(tǒng)會調用其他方法在金字塔中將Activity狀態(tài)下移士骤,從而銷毀Activity。在有些情況下蕾域,Activity將只在金字塔中部分下移并等待(比如拷肌,當用戶切換到其他應用時),Activity可從該點開始移回頂端(如果用戶返回到該Activity)旨巷,并在用戶停止的位置繼續(xù)巨缘。
圖 1.簡化的Activity生命周期圖示,以階梯金字塔表示采呐。此圖示顯示若锁,對于用于將Activity朝頂端的“繼續(xù)”狀態(tài)移動一階的每個回調,有一種將Activity下移一階的回調方法斧吐。Activity還可以從“暫陀止蹋”和“停止”狀態(tài)回到繼續(xù)狀態(tài)。*
根據(jù)Activity的復雜程度煤率,您可能不需要實現(xiàn)所有生命周期方法仰冠。但是,了解每個方法并實現(xiàn)確保您的應用按照用戶期望的方式運行的方法非常重要蝶糯。正確實現(xiàn)您的Activity生命周期方法可確保您的應用按照以下幾種方式良好運行洋只,包括:
- 如果用戶在使用您的應用時接聽來電或切換到另一個應用,它不會崩潰。
- 在用戶未主動使用它時不會消耗寶貴的系統(tǒng)資源木张。
- 如果用戶離開您的應用并稍后返回众辨,不會丟失用戶的進度。
- 當屏幕在橫向和縱向之間旋轉時舷礼,不會崩潰或丟失用戶的進度鹃彻。
正如您將要在以下課程中要學習的,有Activity會在圖 1 所示不同狀態(tài)之間過渡的幾種情況妻献。但是蛛株,這些狀態(tài)中只有三種可以是靜態(tài)。 也就是說育拨,Activity只能在三種狀態(tài)之一下存在很長時間谨履。
- Resumed:在這種狀態(tài)下,Activity處于前臺熬丧,且用戶可以與其交互笋粟。(有時也稱為“運行”狀態(tài)。)
- Paused:在這種狀態(tài)下析蝴,Activity被在前臺中處于半透明狀態(tài)或者未覆蓋整個屏幕的另一個Activity—部分阻擋害捕。暫停的Activity不會接收用戶輸入并且無法執(zhí)行任何代碼。
- Stopped:在這種狀態(tài)下闷畸,Activity被完全隱藏并且對用戶不可見尝盼;它被視為處于后臺。停止時佑菩,Activity實例及其諸如成員變量等所有狀態(tài)信息將保留盾沫,但它無法執(zhí)行任何代碼。
其他狀態(tài)(“創(chuàng)建”和“開始”)是瞬態(tài)殿漠,
其它狀態(tài) (Created與Started)都是短暫的瞬態(tài)赴精,系統(tǒng)會通過調用下一個生命周期回調方法從這些狀態(tài)快速移到下一個狀態(tài)。 也就是說凸舵,在系統(tǒng)調用 onCreate() 之后祖娘,它會快速調用 onStart(),緊接著快速調用 onResume()啊奄。
不論這個activity是當用戶點擊應用圖標創(chuàng)建的main activtiy還是為了響應用戶行為而創(chuàng)建的其他activity,系統(tǒng)都會調用新activity實例中的onCreate()方法掀潮。
我們必須實現(xiàn)onCreate()方法來執(zhí)行程序啟動所需要的基本邏輯菇夸。例如可以在onCreate()方法中定義UI以及實例化類成員變量。
一旦onCreate 操作完成仪吧,系統(tǒng)會迅速調用onStart() 與onResume()方法庄新。我們的activity不會在Created或者Started狀態(tài)停留。技術上來說, activity在onStart()被調用后開始被用戶可見,但是 onResume()會迅速被執(zhí)行使得activity停留在Resumed狀態(tài)择诈,直到一些因素發(fā)生變化才會改變這個狀態(tài)械蹋。例如接收到一個來電,用戶切換到另外一個activity羞芍,或者是設備屏幕關閉
上圖顯示了onCreate(), onStart() 和 onResume()是如何執(zhí)行的哗戈。當這三個順序執(zhí)行的回調函數(shù)完成后,activity會到達Resumed狀態(tài)荷科。
銷毀Activity大多數(shù) app并不需要實現(xiàn)這個方法唯咬,因為局部類的references會隨著activity的銷毀而銷毀,并且我們的activity應該在onPause()與onStop()中執(zhí)行清除activity資源的操作畏浆。然而胆胰,如果activity含有在onCreate調用時創(chuàng)建的后臺線程,或者是其他有可能導致內存泄漏的資源刻获,則應該在OnDestroy()時進行資源清理蜀涨,殺死后臺線程。
@Override
public void onDestroy() {
super.onDestroy(); // Always call the superclass
// Stop method tracing that the activity started during onCreate()
android.os.Debug.stopMethodTracing();
}
Note: 除非程序在onCreate()方法里面就調用了finish()方法蝎毡,系統(tǒng)通常是在執(zhí)行了onPause()與onStop() 之后再調用onDestroy() 厚柳。在某些情況下,例如我們的activity只是做了一個臨時的邏輯跳轉的功能顶掉,它只是用來決定跳轉到哪一個activity草娜,這樣的話,需要在onCreate里面調用finish方法痒筒,這樣系統(tǒng)會直接調用onDestory宰闰,跳過生命周期中的其他方法。
暫停與恢復Activity
在正常使用app時簿透,前端的activity有時會被其他可見的組件阻塞(obstructed)移袍,從而導致當前的activity進入Pause狀態(tài)。例如老充,當打開一個半透明的activity時(例如以對話框的形式)葡盗,之前的activity會被暫停。 只要之前的activity仍然被部分可見啡浊,這個activity就會一直處于Paused狀態(tài)觅够。
然而,一旦之前的activity被完全阻塞并不可見時巷嚣,則其會進入Stop狀態(tài)喘先。
activity一旦進入paused狀態(tài),系統(tǒng)就會調用activity中的onPause()方法, 該方法中可以停止不應該在暫停過程中執(zhí)行的操作廷粒,如暫停視頻播放窘拯;或者保存那些有可能需要長期保存的信息红且。如果用戶從暫停狀態(tài)回到當前activity,系統(tǒng)應該恢復那些數(shù)據(jù)并執(zhí)行onResume()方法涤姊。
Note: 當我們的activity收到調用onPause()的信號時暇番,那可能意味者activity將被暫停一段時間,并且用戶很可能回到我們的activity思喊。然而壁酬,那也是用戶要離開我們的activtiy的第一個信號。
當一個半透明的activity阻塞activity時搔涝,系統(tǒng)會調用onPause()方法并且這個activity會停留在Paused 狀態(tài)(1). 如果用戶在這個activity還是在Paused 狀態(tài)時回到這個activity厨喂,系統(tǒng)則會調用它的onResume() (2).
暫停Activity
當系統(tǒng)調用activity中的onPause(),從技術上講庄呈,意味著activity仍然處于部分可見的狀態(tài).但更多時候意味著用戶正在離開這個activity蜕煌,并馬上會進入Stopped state. 通常應該在onPause()回調方法里面做以下事情:
- 停止動畫或者是其他正在運行的操作,那些都會導致CPU的浪費.
- 提交在用戶離開時期待保存的內容(例如郵件草稿).
- 釋放系統(tǒng)資源诬留,例如broadcast receivers, sensors (比如GPS), 或者是其他任何會影響到電量的資源斜纪。
例如, 如果程序使用Camera,onPause()會是一個比較好的地方去做那些釋放資源的操作。
@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;
}
}
通常文兑,不應該使用onPause()來保存用戶改變的數(shù)據(jù) (例如填入表格中的個人信息) 到永久存儲(File或者DB)上盒刚。僅僅當確認用戶期待那些改變能夠被自動保存的時候(例如正在撰寫郵件草稿),才把那些數(shù)據(jù)存到永久存儲 绿贞。但是因块,我們應該避免在onPause()時執(zhí)行CPU-intensive 的工作,例如寫數(shù)據(jù)到DB籍铁,因為它會導致切換到下一個activity變得緩慢(應該把那些heavy-load的工作放到onStop()去做)涡上。
如果activity實際上是要被Stop,那么我們應該為了切換的順暢而減少在OnPause()方法里面的工作量拒名。
Note:當activity處于暫停狀態(tài)吩愧,Activity實例是駐留在內存中的,并且在activity 恢復的時候重新調用增显。我們不需要在恢復到Resumed狀態(tài)的一系列回調方法中重新初始化組件雁佳。
恢復activity
當用戶從Paused狀態(tài)恢復activity時,系統(tǒng)會調用onResume()方法同云。
請注意糖权,系統(tǒng)每次調用這個方法時,activity都處于前臺炸站,包括第一次創(chuàng)建的時候温兼。所以,應該實現(xiàn)onResume()來初始化那些在onPause方法里面釋放掉的組件武契,并執(zhí)行那些activity每次進入Resumed state都需要的初始化動作 (例如開始動畫與初始化那些只有在獲取用戶焦點時才需要的組件)
下面的onResume()的例子是與上面的onPause()例子相對應的。
@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
恰當?shù)耐V古c重啟我們的activity是很重要的,在activity生命周期中咒唆,他們能確保用戶感知到程序的存在并不會丟失他們的進度届垫。在下面一些關鍵的場景中會涉及到停止與重啟:
- 用戶打開最近使用app的菜單并從我們的app切換到另外一個app,這個時候我們的app是被停止的全释。如果用戶通過手機主界面的啟動程序圖標或者最近使用程序的窗口回到我們的app装处,那么我們的activity會重啟。
- 用戶在我們的app里面執(zhí)行啟動一個新activity的操作浸船,當前activity會在第二個activity被創(chuàng)建后stop妄迁。如果用戶點擊back按鈕,第一個activtiy會被重啟李命。
- 用戶在使用我們的app時接收到一個來電通話.
Activity類提供了onStop()與onRestart()方法來允許在activity停止與重啟時進行調用登淘。不同于暫停狀態(tài)的部分阻塞UI,停止狀態(tài)是UI不再可見并且用戶的焦點轉移到另一個activity中.
Note: 因為系統(tǒng)在activity停止時會在內存中保存Activity的實例封字,所以有時不需要實現(xiàn)onStop(),onRestart()甚至是onStart()方法. 因為大多數(shù)的activity相對比較簡單黔州,activity會自己停止與重啟,我們只需要使用onPause()來停止正在運行的動作并斷開系統(tǒng)資源鏈接阔籽。
上圖顯示:當用戶離開我們的activity時流妻,系統(tǒng)會調用onStop()來停止activity (1). 這個時候如果用戶返回,系統(tǒng)會調用onRestart()(2), 之后會迅速調用onStart()(3)與onResume()(4). 請注意:無論什么原因導致activity停止笆制,系統(tǒng)總是會在onStop()之前調用onPause()方法
停止activity
當activity調用onStop()方法, activity不再可見绅这,并且應該釋放那些不再需要的所有資源。一旦activity停止了在辆,系統(tǒng)會在需要內存空間時摧毀它的實例(和棧結構有關证薇,通常back操作會導致前一個activity被銷毀)。極端情況下开缎,系統(tǒng)會直接殺死我們的app進程棕叫,并不執(zhí)行activity的onDestroy()回調方法, 因此我們需要使用onStop()來釋放資源,從而避免內存泄漏奕删。(這點需要注意)
盡管onPause()方法是在onStop()之前調用俺泣,我們應該使用onStop()來執(zhí)行那些CPU intensive的shut-down操作,例如往數(shù)據(jù)庫寫信息完残。
例如伏钠,下面是一個在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對象會保存在內存中谨设,并在activity resume時被重新調用熟掂。我們不需要在恢復到Resumed state狀態(tài)前重新初始化那些被保存在內存中的組件。系統(tǒng)同樣保存了每一個在布局中的視圖的當前狀態(tài)扎拣,如果用戶在EditText組件中輸入了text赴肚,它會被保存素跺,因此不需要保存與恢復它。
Note: 即使系統(tǒng)會在activity stop時停止這個activity誉券,它仍然會保存View對象的狀態(tài)(比如EditText中的文字) 到一個Bundle中指厌,并且在用戶返回這個activity時恢復它們(下一小節(jié)會介紹在activity銷毀與重新建立時如何使用Bundle來保存其他數(shù)據(jù)的狀態(tài)).
啟動與重啟activity
當activity從Stopped狀態(tài)回到前臺時,它會調用onRestart().系統(tǒng)再調用onStart()方法踊跟,onStart()方法會在每次activity可見時都會被調用踩验。onRestart()方法則是只在activity從stopped狀態(tài)恢復時才會被調用,因此我們可以使用它來執(zhí)行一些特殊的恢復(restoration)工作商玫,請注意之前是被stopped而不是destrory箕憾。
使用onRestart()來恢復activity狀態(tài)是不太常見的,因此對于這個方法如何使用沒有任何的guidelines拳昌。然而袭异,因為onStop()方法應該做清除所有activity資源的操作,我們需要在重啟activtiy時重新實例化那些被清除的資源地回,同樣, 我們也需要在activity第一次創(chuàng)建時實例化那些資源扁远。介于上面的原因,應該使用onStart()作為onStop()所對應方法刻像。因為系統(tǒng)會在創(chuàng)建activity與從停止狀態(tài)重啟activity時都會調用onStart()畅买。也就是說,我們在onStop里面做了哪些清除的操作细睡,就該在onStart里面重新把那些清除掉的資源重新創(chuàng)建出來谷羞。
例如:因為用戶很可能在回到這個activity之前已經(jīng)過了很長一段時間,所以onStart()方法是一個比較好的地方來驗證某些必須的系統(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
}
當系統(tǒng)Destory我們的activity湃缎,它會為activity調用onDestroy()方法。因為我們會在onStop方法里面做釋放資源的操作蠢壹,那么onDestory方法則是我們最后去清除那些可能導致內存泄漏的地方嗓违。因此需要確保那些線程都被destroyed并且所有的操作都被停止。
重新創(chuàng)建Activity
有幾個場景中图贸,Activity是由于正常的程序行為而被Destory的蹂季。例如當用戶點擊返回按鈕或者是Activity通過調用finish()來發(fā)出停止信號。系統(tǒng)也有可能會在Activity處于stop狀態(tài)且長時間不被使用疏日,或者是在前臺activity需要更多系統(tǒng)資源的時關閉后臺進程偿洁,以圖獲取更多的內存。
當Activity是因為用戶點擊Back按鈕或者是activity通過調用finish()結束自己時沟优,系統(tǒng)就丟失了對Activity實例的引用涕滋,因為這一行為意味著不再需要這個activity了。然而挠阁,如果因為系統(tǒng)資源緊張而導致Activity的Destory宾肺, 系統(tǒng)會在用戶回到這個Activity時有這個Activity存在過的記錄溯饵,系統(tǒng)會使用那些保存的記錄數(shù)據(jù)(描述了當Activity被Destory時的狀態(tài))來重新創(chuàng)建一個新的Activity實例。那些被系統(tǒng)用來恢復之前狀態(tài)而保存的數(shù)據(jù)被叫做 "instance state" 爱榕,它是一些存放在Bundle對象中的key-value pairs瓣喊。(請注意這里的描述,這對理解onSaveInstanceState執(zhí)行的時刻很重要)
Caution: 你的Activity會在每次旋轉屏幕時被destroyed與recreated黔酥。當屏幕改變方向時,系統(tǒng)會Destory與Recreate前臺的activity洪橘,因為屏幕配置被改變跪者,你的Activity可能需要加載另一些替代的資源(例如layout).
默認情況下, 系統(tǒng)使用 Bundle 實例來保存每一個View(視圖)對象中的信息(例如輸入EditText 中的文本內容)。因此熄求,如果Activity被destroyed與recreated, 則layout的狀態(tài)信息會自動恢復到之前的狀態(tài)渣玲。然而,activity也許存在更多你想要恢復的狀態(tài)信息弟晚,例如記錄用戶Progress的成員變量(member variables)忘衍。
Note: 為了使Android系統(tǒng)能夠恢復Activity中的View的狀態(tài),每個View都必須有一個唯一ID卿城,由android:id定義枚钓。
為了可以保存額外更多的數(shù)據(jù)到saved instance state。在Activity的生命周期里面存在一個額外的回調函數(shù)瑟押,你必須重寫這個函數(shù)搀捷。該回調函數(shù)并沒有在前面課程的圖片示例中顯示。這個方法是onSaveInstanceState() 多望,當用戶離開Activity時嫩舟,系統(tǒng)會調用它。當系統(tǒng)調用這個函數(shù)時怀偷,系統(tǒng)會在Activity被異常Destory時傳遞 Bundle 對象家厌,這樣我們就可以增加額外的信息到Bundle中并保存到系統(tǒng)中。若系統(tǒng)在Activity被Destory之后想重新創(chuàng)建這個Activity實例時椎工,之前的Bundle對象會(系統(tǒng))被傳遞到你我們activity的onRestoreInstanceState()方法與 onCreate() 方法中饭于。
當系統(tǒng)開始停止Activity時,只有在Activity實例會需要重新創(chuàng)建的情況下才會調用到onSaveInstanceState() (1) 晋渺,在這個方法里面可以指定額外的狀態(tài)數(shù)據(jù)到Bunde中镰绎。如果這個Activity被destroyed然后這個實例又需要被重新創(chuàng)建時,系統(tǒng)會傳遞在 (1) 中的狀態(tài)數(shù)據(jù)到 onCreate() (2) 與 onRestoreInstanceState()(3).
保存Activity狀態(tài)
當我們的activity開始Stop木西,系統(tǒng)會調用 onSaveInstanceState() 畴栖,Activity可以用鍵值對的集合來保存狀態(tài)信息。這個方法會默認保存Activity視圖的狀態(tài)信息八千,如在 EditText 組件中的文本或 ListView 的滑動位置吗讶。
為了給Activity保存額外的狀態(tài)信息燎猛,你必須實現(xiàn)onSaveInstanceState() 并增加key-value pairs到 Bundle 對象中,例如:
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);
}
Caution: 必須要調用 onSaveInstanceState() 方法的父類實現(xiàn)照皆,這樣默認的父類實現(xiàn)才能保存視圖狀態(tài)的信息重绷。
恢復Activity狀態(tài)
當Activity從Destory中重建,我們可以從系統(tǒng)傳遞的Activity的Bundle中恢復保存的狀態(tài)膜毁。 onCreate() 與 onRestoreInstanceState() 回調方法都接收到了同樣的Bundle昭卓,里面包含了同樣的實例狀態(tài)信息。
由于 onCreate() 方法會在第一次創(chuàng)建新的Activity實例與重新創(chuàng)建之前被Destory的實例時都被調用瘟滨,我們必須在嘗試讀取 Bundle 對象前檢測它是否為null候醒。如果它為null,系統(tǒng)則是創(chuàng)建一個新的Activity實例杂瘸,而不是恢復之前被Destory的Activity倒淫。
下面是一個示例:演示在onCreate方法里面恢復一些數(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
}
...
}
我們也可以選擇實現(xiàn) onRestoreInstanceState() ,而不是在onCreate方法里面恢復數(shù)據(jù)败玉。 onRestoreInstanceState()方法會在 onStart() 方法之后執(zhí)行. 系統(tǒng)僅僅會在存在需要恢復的狀態(tài)信息時才會調用 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);
}
Caution: 與上面保存一樣运翼,總是需要調用onRestoreInstanceState()方法的父類實現(xiàn)返干,這樣默認的父類實現(xiàn)才能保存視圖狀態(tài)的信息。更多關于運行時狀態(tài)改變引起的recreate我們的activity南蹂。請參考Handling Runtime Changes.