Android学习笔记系列四2 —— Activity的生命周期
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
啟動一個(gè)Activity
使用startActivity(Intent intent)。 intent指定了你想要啟動的activity,或者指定了你想展現(xiàn)的動作(系統(tǒng)幫你選擇合適的activity,它可能來自于其他的程序)。 intent也可以攜帶比較小量的數(shù)據(jù),用于啟動acitivity。
在你自己的應(yīng)用中,你經(jīng)常會通過創(chuàng)建一個(gè)明確的intent,簡單地啟動一個(gè)已知的activity,。這個(gè)intent指定了activity的類名。 例如下面演示了如何啟動一個(gè)叫SignInActivity的activity:
Intent?intent?=?new?Intent(this,?SignInActivity.class); startActivity(intent);然而,你的程序可能想要展示某些動作,例如發(fā)郵件,短信,微博,或者使用你activity中的數(shù)據(jù)。 這時(shí)候,你就不應(yīng)該使用自己的activity來做這些工作。你應(yīng)該調(diào)用系統(tǒng)中其他程序提供的響應(yīng)功能。 這是intent真正體現(xiàn)其價(jià)值的地方。你可以創(chuàng)建一個(gè)描述了響應(yīng)動作的intent,然后系統(tǒng)來為你挑選完成任務(wù)的程序。 如果有多個(gè)選擇,系統(tǒng)會提示用戶進(jìn)行選擇。例如你想讓用戶發(fā)郵件,你可以創(chuàng)建下面的intent:
Intent?intent?=?new?Intent(Intent.ACTION_SEND); intent.putExtra(Intent.EXTRA_EMAIL,?recipientArray); startActivity(intent);EXTRA_EMAIL?是一個(gè)郵件intent中添加的額外字符串?dāng)?shù)組,它指定了郵件該發(fā)給哪些郵件地址。當(dāng)一個(gè)郵件程序響應(yīng)了這個(gè)intent, 它將讀取這些地址,并把他們放置到郵件表單的被發(fā)送人欄。這時(shí),郵件程序被啟動。當(dāng)用戶完成了發(fā)送操作,你的activity會被恢復(fù)。
啟動一個(gè)帶返回結(jié)果的activity
有時(shí)候,你想要啟動一個(gè)activity,并從這個(gè)activty獲得一個(gè)結(jié)果。 這時(shí),要通過?startActivityForResult()?(取代startActivity()) 來啟動activity。 然后通過實(shí)現(xiàn)onActivityResult()回調(diào)方法來獲得返回后的結(jié)果。 當(dāng)這個(gè)后續(xù)的activity被關(guān)閉,它將發(fā)送一個(gè)?Intent?給?onActivityResult()?方法。
例如,你可能想要取一個(gè)聯(lián)系人的信息。下面介紹怎么創(chuàng)建intent并處理結(jié)果:
private?void?pickContact()?{//?Create?an?intent?to?"pick"?a?contact,?as?defined?by?the?content?provider?URIIntent?intent?=?new?Intent(Intent.ACTION_PICK,?Contacts.CONTENT_URI);startActivityForResult(intent,?PICK_CONTACT_REQUEST); }@Override protected?void?onActivityResult(int?requestCode,?int?resultCode,?Intent?data)?{//?If?the?request?went?well?(OK)?and?the?request?was?PICK_CONTACT_REQUESTif?(resultCode?==?Activity.RESULT_OK?&&?requestCode?==?PICK_CONTACT_REQUEST)?{//?Perform?a?query?to?the?contact's?content?provider?for?the?contact's?nameCursor?cursor?=?getContentResolver().query(data.getData()?,?new?String[]?{Contacts.DISPLAY_NAME},?null,?null,?null);if?(cursor.moveToFirst())?{?//?True?if?the?cursor?is?not?emptyint?columnIndex?=?cursor.getColumnIndex(Contacts.DISPLAY_NAME);String?name?=?cursor.getString(columnIndex);//?Do?something?with?the?selected?contact's?name...}} }這個(gè)例子展示了使用onActivityResult()?來獲取結(jié)果的基本方法。 第一步要判斷請求是否被成功響應(yīng),通過判斷resultCode?是不是RESULT_OK, 然后判斷這個(gè)響應(yīng)是不是針對相應(yīng)的請求,此時(shí)只要判斷requestCode和發(fā)送時(shí)提供的第二個(gè)參數(shù)?startActivityForResult()?是否相匹配。 最后,查詢?Intent中的data信息。 (data?參數(shù))。
這個(gè)過程中,ContentResolver?開啟了一個(gè)查詢而不是content provider, 它返回一個(gè)?Cursor?,這將允許數(shù)據(jù)被讀取。更多content provider相關(guān)信息,請查閱Content Providers?文檔。
關(guān)于intent的更多信息, 查看?Intents and Intent Filters?文檔。
關(guān)閉Activity
你可以通過調(diào)用finish()?來終止activity。 你也可以調(diào)用finishActivity()?來終止你之前啟動了的一個(gè)獨(dú)立activity。
注意:?多數(shù)情況下,你不應(yīng)該明確地通過這些方式來關(guān)閉acitivity。 就像下面要討論的activity的生命周期。系統(tǒng)會為你管理。所以你不必關(guān)閉他們。 調(diào)用這些方法將有悖于用戶體驗(yàn)。它們僅用于你絕對不想讓用戶再返回這個(gè)activity的實(shí)例。
管理Activity的生命周期
activity可能處于三種基本的狀態(tài):
Resumedactivity????在屏幕的前臺并且擁有用戶的焦點(diǎn)。(這個(gè)狀態(tài)有時(shí)也被叫做“running”)
Paused????另一個(gè)activity在前臺并擁有焦點(diǎn),但是本activity還是可見的。 也就是說,另外一個(gè)activity覆蓋在本activity的上面,并且那個(gè)activity是部分透明的或沒有覆蓋整個(gè)屏幕。 一個(gè)paused的activity是完全存活的(Activity 對象仍然保留在內(nèi)存里,它保持著所有的狀態(tài)和成員信息,并且保持與window manager的聯(lián)接),但在系統(tǒng)內(nèi)存嚴(yán)重不足的情況下它能被殺死。
Stopped????本activity被其它的activity完全遮擋住了(本activity目前在后臺)。 一個(gè)stopped的activity也仍然是存活的(Activity 對象仍然保留在內(nèi)存中,它保持著所有的狀態(tài)和成員信息,但是不再與window manager聯(lián)接了)。 但是,對于用戶而言它已經(jīng)不再可見了,并且當(dāng)其它地方需要內(nèi)存時(shí)它將會被殺死。
如果activity被paused或stopped了,則系統(tǒng)可以從內(nèi)存中刪除它,通過請求finish(調(diào)用它的 finish() 方法)或者直接殺死它的進(jìn)程。 當(dāng)這個(gè)activity被再次啟動時(shí)(在被finish或者kill后),它必須被完全重建。
實(shí)現(xiàn)生命周期回調(diào)方法
當(dāng)一個(gè)activity在上述描述的狀態(tài)之間轉(zhuǎn)換時(shí),它將通過各種回調(diào)方法來獲得通知。 所有的回調(diào)方法都是鉤子(hook),當(dāng)activity狀態(tài)發(fā)生改變時(shí)你都可以 重寫這些方法來執(zhí)行對應(yīng)的工作。 以下的activity提綱包含了所有基本的生命周期方法:
public?class?ExampleActivity?extends?Activity?{?@Override?public?void?onCreate(Bundle?savedInstanceState)?{?super.onCreate(savedInstanceState);//?The?activity?is?being?created.?}@Override?protected?void?onStart()?{super.onStart();//?The?activity?is?about?to?become?visible.}?@Override?protected?void?onResume()?{?super.onResume();//?The?activity?has?become?visible?(it?is?now?"resumed").?}?@Override?protected?void?onPause()?{?super.onPause();?//?Another?activity?is?taking?focus?(this?activity?is?about?to?be?"paused").?}?@Override?protected?void?onStop()?{?super.onStop();?//?The?activity?is?no?longer?visible?(it?is?now?"stopped")?}?@Override?protected?void?onDestroy()?{?super.onDestroy();?//?The?activity?is?about?to?be?destroyed.?}? }注意:實(shí)現(xiàn)這些生命周期方法時(shí),必須保證在其它代碼之前首先調(diào)用一下父類的方法,如上例所示。
總體來講,這些方法定義了一個(gè)activity的完整的生命周期。 通過實(shí)現(xiàn)這些方法,你可以監(jiān)控activity生命周期中三個(gè)嵌套的循環(huán):
activity的完整生存期會在 onCreate() 調(diào)用和 onDestroy() 調(diào)用之間發(fā)生。 你的activity應(yīng)該在 onCreate() 方法里完成所有“全局global”狀態(tài)的設(shè)置(比如定義layout), 而在onDestroy() 方法里釋放所有占用的資源。 例如,如果你的activity有一個(gè)后臺運(yùn)行的線程,用于從網(wǎng)絡(luò)下載數(shù)據(jù),那么你應(yīng)該在 onCreate() 方法里創(chuàng)建這個(gè)線程并且在 onDestroy() 方法里停止這個(gè)線程。
activity的可見生存期會在 onStart() 調(diào)用和 onStop() 調(diào)用之間發(fā)生。在這期間,用戶可在屏幕上看見這個(gè)activity并可與之交互。 例如,當(dāng)一個(gè)新的activity啟動后調(diào)用了 onStop() 方法,則這個(gè)activity就無法被看見了。 在這兩個(gè)方法之間,你可以管理那些顯示activity所需的資源。例如,你可以在 onStart() 方法里注冊一個(gè) BroadcastReceiver 用于監(jiān)控影響用戶界面的改動。并且當(dāng)用戶不再看到你的顯示內(nèi)容時(shí),在 onStop() 方法里注銷掉它。 系統(tǒng)會在activity的整個(gè)生存期內(nèi)多次調(diào)用 onStart() 和onStop(), 因?yàn)閍ctivity可能會在顯示和隱藏之間不斷地來回切換。
activity的前臺生存期會在 onResume() 調(diào)用和 onPause() 之間發(fā)生。在這期間,activity是位于屏幕上所有其它的activity之前,并且擁有用戶的輸入焦點(diǎn)。 activity可以頻繁地進(jìn)入和退出前臺——例如, 當(dāng)設(shè)備進(jìn)入休眠時(shí)或者彈出一個(gè)對話框時(shí), onPause() 就會被調(diào)用。因?yàn)檫@個(gè)狀態(tài)可能會經(jīng)常發(fā)生轉(zhuǎn)換,為了避免切換遲緩引起的用戶等待,這兩個(gè)方法中的代碼應(yīng)該相當(dāng)?shù)剌p量化。
圖1說明了activity在狀態(tài)之間可能行走的這些循環(huán)和路徑。矩形代表了你可以實(shí)現(xiàn)的回調(diào)方法,用于activity狀態(tài)轉(zhuǎn)換時(shí)執(zhí)行相應(yīng)操作。
同樣的生命周期回調(diào)方法已經(jīng)在表1中列出了,該表更詳細(xì)地描述了每個(gè)回調(diào)方法,并且指明了每個(gè)方法在activity的全生命周期中的位置, 包括回調(diào)方法完成后系統(tǒng)是否會殺死這個(gè)activity。
表 1.activity生命周期回調(diào)方法匯總
| onCreate() | activity第一次被創(chuàng)建時(shí)調(diào)用。在這里你應(yīng)該完成所有常見的靜態(tài)設(shè)置工作——?jiǎng)?chuàng)建view、綁定list數(shù)據(jù)等等。 本方法傳入一個(gè)包含了該activity前一個(gè)狀態(tài)的Bundle對象(如果之前已捕獲了狀態(tài)的話,詳見后面的保存Activity狀態(tài))。 下一個(gè)回調(diào)方法總是onStart()。 | 否 | onStart() |
| onRestart() | activity被停止后、又再次被啟動之前調(diào)用。 下一個(gè)回調(diào)方法總是onStart() | 否 | onStart() |
| onStart() | activity要顯示給用戶之前調(diào)用。 如果activity進(jìn)入前臺,則下一個(gè)回調(diào)方法是onResume();如果進(jìn)入隱藏狀態(tài),則下一個(gè)回調(diào)方法是onStop()。 | 否 | onResume() 或 onStop() |
| onResume() | activity開始與用戶交互之前調(diào)用。這時(shí)activity是在activity棧的頂端,用戶可以向其中輸入。 下一個(gè)回調(diào)方法總是onPause()。 | 否 | onPause() |
| onPause() | 當(dāng)系統(tǒng)準(zhǔn)備啟動另一個(gè)正在恢復(fù)的activity時(shí)調(diào)用。這個(gè)方法通常用于把未保存的改動提交為永久數(shù)據(jù)、停止動畫播放、以及其它可能消耗CPU的工作等等。 它應(yīng)該非常迅速地完成工作,因?yàn)橄乱粋€(gè)activity在本方法返回前是不會被恢復(fù)運(yùn)行的。 如果activity返回前臺,則下一個(gè)回調(diào)方法是onResume();如果進(jìn)入用戶不可見狀態(tài),則下一個(gè)是onStop() | 可以 | onResume() 或 onStop() |
| onStop() | 當(dāng)activity不再對用戶可見時(shí)調(diào)用。原因可能是它即將被銷毀、或者其它activity(已有或新建的)被恢復(fù)運(yùn)行并要覆蓋本activity。 如果activity還會回來與用戶交互,則下一個(gè)回調(diào)方法是onRestart();如果這個(gè)activity即將消失,則下一個(gè)回調(diào)方法是onDestroy() | 可以 | onRestart() 或 onDestroy() |
| onDestroy() | 在本activity被銷毀前調(diào)用。這是activity收到的最后一個(gè)調(diào)用。 可能是因?yàn)閍ctivity完成了工作(有些人在這里調(diào)用finish()), 也可能是因?yàn)橄到y(tǒng)為了騰出空間而臨時(shí)銷毀activity的本實(shí)例。 可以利用isFinishing() 方法來區(qū)分這兩種情況。 | 可以 | 無 |
標(biāo)為“之后可否被殺死?”的列指明了系統(tǒng)是否可以在這個(gè)方法返回之后的任意時(shí)刻殺掉這個(gè)activity的宿主進(jìn)程, 而不再執(zhí)行其它流程上的activity代碼。 有三個(gè)方法是標(biāo)為“可以”:( onPause()、 onStop()、 和onDestroy())。 因?yàn)閛nPause()是三個(gè)方法中的第一個(gè), 一旦activity被創(chuàng)建, onPause() 就是進(jìn)程可以被殺死之前最后一個(gè)能確保被調(diào)用的方法 ——如果系統(tǒng)在某種緊急情況下必須回收內(nèi)存,則 onStop() 和onDestroy() 可能就不會被調(diào)用了。因此,你應(yīng)該使用 onPause() 來把至關(guān)重要的需長期保存的數(shù)據(jù)寫入存儲器(比如用戶所編輯的內(nèi)容)。 不過,應(yīng)該對必須通過 onPause() 方法進(jìn)行保存的信息有所選擇,因?yàn)樵摲椒ㄖ兴械淖枞僮鞫紩屒袚Q到下一個(gè)activity的停滯,并使用戶感覺到遲緩。
“之后可否被殺死?”列中標(biāo)為“否”的方法,在它們被調(diào)用時(shí)的那一刻起,就會保護(hù)本activity的宿主進(jìn)程不被殺掉。 因此,只有在 onPause() 方法返回時(shí)至 onResume() 方法被調(diào)用時(shí)之間,activity才會被殺掉。直到 onPause() 再次被調(diào)用并返回時(shí),activity都不會再次允許被殺死。
Note:表1中標(biāo)明的技術(shù)上不“可殺”的activity仍然有可能會被系統(tǒng)殺死——但那只有在沒有任何資源的極端情況下才會發(fā)生。 什么時(shí)候activity會被殺掉,已在文檔進(jìn)程和線程里詳細(xì)說明了。
保存activity的狀態(tài)
管理Activity生命周期一節(jié)中已簡單提到,當(dāng)一個(gè)activity被paused或者stopped時(shí),activity的狀態(tài)可以被保存。 的確如此,因?yàn)?Activity 對象在paused或者stopped時(shí)仍然被保留在內(nèi)存之中——它所有的成員信息和當(dāng)前狀態(tài)都仍然存活。 這樣用戶在activity里所作的改動全都還保存著,所以當(dāng)activity返回到前臺時(shí)(當(dāng)它“resume“),那些改動仍然有效。
不過,如果系統(tǒng)是為了回收內(nèi)存而銷毀activity,則這個(gè) Activity 對象就會被銷毀,這樣系統(tǒng)就無法簡單地resume一下就能還原完整狀態(tài)的activity。 如果用戶要返回到這個(gè)activity的話,系統(tǒng)必須重新創(chuàng)建這個(gè)Activity 對象??墒怯脩舨⒉恢老到y(tǒng)是先銷毀activity再重新創(chuàng)建了它的,所以,他很可能希望activity完全保持原樣。 這種情況下,你可以保證activity狀態(tài)的相關(guān)重要信息都由另一個(gè)回調(diào)方法保存下來了,此方法讓你能保存activity狀態(tài)的相關(guān)信息: onSaveInstanceState()。
在activity變得很容易被銷毀之前,系統(tǒng)會調(diào)用 onSaveInstanceState()方法。 調(diào)用時(shí)系統(tǒng)會傳入一個(gè)Bundle對象, 你可以利用 putString() 之類的方法,以鍵值對的方式來把a(bǔ)ctivity狀態(tài)信息保存到該Bundle對象中。 然后,如果系統(tǒng)殺掉了你的application進(jìn)程并且用戶又返回到你的activity,系統(tǒng)就會重建activity并將這個(gè) Bundle 傳入onCreate() 和onRestoreInstanceState() 中,你就可以從 Bundle 中解析出已保存信息并恢復(fù)activity狀態(tài)。如果沒有儲存狀態(tài)信息,那么傳入的 Bundle 將為null(當(dāng)activity第一次被創(chuàng)建時(shí)就是如此)。
注意:?activity被銷毀之前,并不能確保每次都會調(diào)用 onSaveInstanceState() ,因?yàn)榇嬖谀切┎槐乇4鏍顟B(tài)的情況(比如用戶使用BACK鍵離開了你的activity,因?yàn)橛脩裘黠@是關(guān)了這個(gè)activity)。 如果系統(tǒng)要調(diào)用 onSaveInstanceState() 方法,那么它通常會在 onStop() 方法之前并且可能是在 onPause() 之前調(diào)用。
不過,即使你沒有實(shí)現(xiàn) onSaveInstanceState() 方法,有些activity狀態(tài)還是會通過 Activity 類默認(rèn)實(shí)現(xiàn)的onSaveInstanceState() 方法保存下來。特別的是,默認(rèn)為layout中的每個(gè) View 實(shí)現(xiàn)了調(diào)用相應(yīng)的onSaveInstanceState() 方法,這允許每一個(gè)view提供自己需被保存的信息。 幾乎Android框架下所有的widget都會在適當(dāng)?shù)臅r(shí)候?qū)崿F(xiàn)該方法,這樣,任何UI上可見的變化都會自動保存下來,并在activity重建后自動恢復(fù)。 例如,EditText widget會保存所有用戶已經(jīng)輸入的文本, CheckBoxwidget 也會保存是否被選中。你所要做的工作僅僅是為每一個(gè)你想要保存其狀態(tài)的widget提供一個(gè)唯一的ID(就是 android:id 屬性)。如果這個(gè)widget沒有ID的話,系統(tǒng)是無法保存它們的狀態(tài)的。
通過把a(bǔ)ndroid:saveEnabled 設(shè)置為"false",或者調(diào)用 setSaveEnabled() 方法,你也可以顯式地阻止layout中的某個(gè)view保存狀態(tài)。 通常不應(yīng)該禁用保存,不過假如你需要恢復(fù)activity UI的各個(gè)不同的狀態(tài),也許可以這么做。
盡管缺省實(shí)現(xiàn)的 onSaveInstanceState() 方法會保存activity UI的有用信息,你仍然需要覆蓋它來存入更多的信息。 例如,你可能需要保存在activity生命周期中改變的成員變量值(可能是關(guān)于UI恢復(fù)的值,但是默認(rèn)情況下,存放這些UI狀態(tài)的成員變量值是不會被恢復(fù)的)。
因?yàn)槟J(rèn)實(shí)現(xiàn)的 onSaveInstanceState() 方法已經(jīng)幫你保存了一些UI的狀態(tài),所以如果你重寫此方法是為了保存更多的狀態(tài)信息,那么在執(zhí)行自己的代碼之前應(yīng)該確保先調(diào)用一次父類的 onSaveInstanceState() 方法。同理,如果重寫 onRestoreInstanceState() 的話,也應(yīng)該調(diào)用一次父類的該方法,這樣缺省的代碼就能正常恢復(fù)view的狀態(tài)了。
注意:因?yàn)?onSaveInstanceState() 并不保證每次都會被調(diào)用,所以你應(yīng)該只用它來記錄activity的一些臨時(shí)狀態(tài)信息(UI的狀態(tài))——千萬不要用它來保存那些需要長久保存的數(shù)據(jù)。 替代方案是,你應(yīng)該在用戶離開activity的時(shí)候利用 onPause() 來保存永久性數(shù)據(jù)(比如那些需要存入數(shù)據(jù)庫里的數(shù)據(jù))。
配置改動后的處理
設(shè)備的某些配置可能會在運(yùn)行時(shí)發(fā)生變化(比如屏幕方向、鍵盤可用性以及語言)。 當(dāng)發(fā)生這些變化時(shí),Android會重建這個(gè)運(yùn)行中的activity(系統(tǒng)會調(diào)用 onDestroy() ,然后再馬上調(diào)用 onCreate() )。這種設(shè)計(jì)有助于應(yīng)用程序適用新的參數(shù)配置,通過把你預(yù)置的可替換資源(比如對應(yīng)各種屏幕方向和尺寸的layout)自動重新裝載進(jìn)入應(yīng)用程序的方式來實(shí)現(xiàn)。
如果你采取了適當(dāng)?shù)脑O(shè)計(jì),讓activity能夠正確地處理這些因?yàn)槠聊环较蚨鸬闹貑?#xff0c;并能如上所述地恢復(fù)activity狀態(tài), 那么你的應(yīng)用程序?qū)ι芷谥衅渌囊馔馐录哌m應(yīng)能力。
處理這類重啟的最佳方式,就是利用 onSaveInstanceState() 和onRestoreInstanceState() (或者 onCreate() )進(jìn)行狀態(tài)的保存和恢復(fù)。
多activity的合作
當(dāng)activity啟動另一個(gè)activity時(shí),它倆生命周期的狀態(tài)都會發(fā)生轉(zhuǎn)換。 第一個(gè)activity paused并stopped(盡管它也可能不會被stopped,如果它仍然后臺可見的話),而另一個(gè)activity是被created。 如果這兩個(gè)activity共用了保存在磁盤或其它地方的數(shù)據(jù),那么請明白:在第二個(gè)activity被created之前,第一個(gè)activity還沒有完全被stopped,這點(diǎn)非常重要。第二個(gè)activity的啟動進(jìn)程與第一個(gè)activity的關(guān)閉進(jìn)程在時(shí)間上會發(fā)生重疊。
生命周期回調(diào)方法的順序是很明確的,特別是兩個(gè)activity位于同一個(gè)進(jìn)程中、一個(gè)啟動另一個(gè)的時(shí)候。 下面就是Aactivity A啟動Activity B時(shí)的操作順序:
Activity A的 onPause()方法,如果活動后臺不可見的話,運(yùn)行onStop(),否則不運(yùn)行。
Activity B的 onCreate() ,onStart() 和onResume() 方法依次運(yùn)行。(Activity B現(xiàn)在獲得用戶焦點(diǎn)。)
轉(zhuǎn)載于:https://my.oschina.net/JiangTun/blog/639976
與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的Android学习笔记系列四2 —— Activity的生命周期的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UIImage使用
- 下一篇: lintcode:排颜色 II