Android学习笔记系列四2 —— Activity的生命周期
2019獨角獸企業重金招聘Python工程師標準>>>
啟動一個Activity
使用startActivity(Intent intent)。 intent指定了你想要啟動的activity,或者指定了你想展現的動作(系統幫你選擇合適的activity,它可能來自于其他的程序)。 intent也可以攜帶比較小量的數據,用于啟動acitivity。
在你自己的應用中,你經常會通過創建一個明確的intent,簡單地啟動一個已知的activity,。這個intent指定了activity的類名。 例如下面演示了如何啟動一個叫SignInActivity的activity:
Intent?intent?=?new?Intent(this,?SignInActivity.class); startActivity(intent);然而,你的程序可能想要展示某些動作,例如發郵件,短信,微博,或者使用你activity中的數據。 這時候,你就不應該使用自己的activity來做這些工作。你應該調用系統中其他程序提供的響應功能。 這是intent真正體現其價值的地方。你可以創建一個描述了響應動作的intent,然后系統來為你挑選完成任務的程序。 如果有多個選擇,系統會提示用戶進行選擇。例如你想讓用戶發郵件,你可以創建下面的intent:
Intent?intent?=?new?Intent(Intent.ACTION_SEND); intent.putExtra(Intent.EXTRA_EMAIL,?recipientArray); startActivity(intent);EXTRA_EMAIL?是一個郵件intent中添加的額外字符串數組,它指定了郵件該發給哪些郵件地址。當一個郵件程序響應了這個intent, 它將讀取這些地址,并把他們放置到郵件表單的被發送人欄。這時,郵件程序被啟動。當用戶完成了發送操作,你的activity會被恢復。
啟動一個帶返回結果的activity
有時候,你想要啟動一個activity,并從這個activty獲得一個結果。 這時,要通過?startActivityForResult()?(取代startActivity()) 來啟動activity。 然后通過實現onActivityResult()回調方法來獲得返回后的結果。 當這個后續的activity被關閉,它將發送一個?Intent?給?onActivityResult()?方法。
例如,你可能想要取一個聯系人的信息。下面介紹怎么創建intent并處理結果:
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...}} }這個例子展示了使用onActivityResult()?來獲取結果的基本方法。 第一步要判斷請求是否被成功響應,通過判斷resultCode?是不是RESULT_OK, 然后判斷這個響應是不是針對相應的請求,此時只要判斷requestCode和發送時提供的第二個參數?startActivityForResult()?是否相匹配。 最后,查詢?Intent中的data信息。 (data?參數)。
這個過程中,ContentResolver?開啟了一個查詢而不是content provider, 它返回一個?Cursor?,這將允許數據被讀取。更多content provider相關信息,請查閱Content Providers?文檔。
關于intent的更多信息, 查看?Intents and Intent Filters?文檔。
關閉Activity
你可以通過調用finish()?來終止activity。 你也可以調用finishActivity()?來終止你之前啟動了的一個獨立activity。
注意:?多數情況下,你不應該明確地通過這些方式來關閉acitivity。 就像下面要討論的activity的生命周期。系統會為你管理。所以你不必關閉他們。 調用這些方法將有悖于用戶體驗。它們僅用于你絕對不想讓用戶再返回這個activity的實例。
管理Activity的生命周期
activity可能處于三種基本的狀態:
Resumedactivity????在屏幕的前臺并且擁有用戶的焦點。(這個狀態有時也被叫做“running”)
Paused????另一個activity在前臺并擁有焦點,但是本activity還是可見的。 也就是說,另外一個activity覆蓋在本activity的上面,并且那個activity是部分透明的或沒有覆蓋整個屏幕。 一個paused的activity是完全存活的(Activity 對象仍然保留在內存里,它保持著所有的狀態和成員信息,并且保持與window manager的聯接),但在系統內存嚴重不足的情況下它能被殺死。
Stopped????本activity被其它的activity完全遮擋住了(本activity目前在后臺)。 一個stopped的activity也仍然是存活的(Activity 對象仍然保留在內存中,它保持著所有的狀態和成員信息,但是不再與window manager聯接了)。 但是,對于用戶而言它已經不再可見了,并且當其它地方需要內存時它將會被殺死。
如果activity被paused或stopped了,則系統可以從內存中刪除它,通過請求finish(調用它的 finish() 方法)或者直接殺死它的進程。 當這個activity被再次啟動時(在被finish或者kill后),它必須被完全重建。
實現生命周期回調方法
當一個activity在上述描述的狀態之間轉換時,它將通過各種回調方法來獲得通知。 所有的回調方法都是鉤子(hook),當activity狀態發生改變時你都可以 重寫這些方法來執行對應的工作。 以下的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.?}? }注意:實現這些生命周期方法時,必須保證在其它代碼之前首先調用一下父類的方法,如上例所示。
總體來講,這些方法定義了一個activity的完整的生命周期。 通過實現這些方法,你可以監控activity生命周期中三個嵌套的循環:
activity的完整生存期會在 onCreate() 調用和 onDestroy() 調用之間發生。 你的activity應該在 onCreate() 方法里完成所有“全局global”狀態的設置(比如定義layout), 而在onDestroy() 方法里釋放所有占用的資源。 例如,如果你的activity有一個后臺運行的線程,用于從網絡下載數據,那么你應該在 onCreate() 方法里創建這個線程并且在 onDestroy() 方法里停止這個線程。
activity的可見生存期會在 onStart() 調用和 onStop() 調用之間發生。在這期間,用戶可在屏幕上看見這個activity并可與之交互。 例如,當一個新的activity啟動后調用了 onStop() 方法,則這個activity就無法被看見了。 在這兩個方法之間,你可以管理那些顯示activity所需的資源。例如,你可以在 onStart() 方法里注冊一個 BroadcastReceiver 用于監控影響用戶界面的改動。并且當用戶不再看到你的顯示內容時,在 onStop() 方法里注銷掉它。 系統會在activity的整個生存期內多次調用 onStart() 和onStop(), 因為activity可能會在顯示和隱藏之間不斷地來回切換。
activity的前臺生存期會在 onResume() 調用和 onPause() 之間發生。在這期間,activity是位于屏幕上所有其它的activity之前,并且擁有用戶的輸入焦點。 activity可以頻繁地進入和退出前臺——例如, 當設備進入休眠時或者彈出一個對話框時, onPause() 就會被調用。因為這個狀態可能會經常發生轉換,為了避免切換遲緩引起的用戶等待,這兩個方法中的代碼應該相當地輕量化。
圖1說明了activity在狀態之間可能行走的這些循環和路徑。矩形代表了你可以實現的回調方法,用于activity狀態轉換時執行相應操作。
同樣的生命周期回調方法已經在表1中列出了,該表更詳細地描述了每個回調方法,并且指明了每個方法在activity的全生命周期中的位置, 包括回調方法完成后系統是否會殺死這個activity。
表 1.activity生命周期回調方法匯總
| onCreate() | activity第一次被創建時調用。在這里你應該完成所有常見的靜態設置工作——創建view、綁定list數據等等。 本方法傳入一個包含了該activity前一個狀態的Bundle對象(如果之前已捕獲了狀態的話,詳見后面的保存Activity狀態)。 下一個回調方法總是onStart()。 | 否 | onStart() |
| onRestart() | activity被停止后、又再次被啟動之前調用。 下一個回調方法總是onStart() | 否 | onStart() |
| onStart() | activity要顯示給用戶之前調用。 如果activity進入前臺,則下一個回調方法是onResume();如果進入隱藏狀態,則下一個回調方法是onStop()。 | 否 | onResume() 或 onStop() |
| onResume() | activity開始與用戶交互之前調用。這時activity是在activity棧的頂端,用戶可以向其中輸入。 下一個回調方法總是onPause()。 | 否 | onPause() |
| onPause() | 當系統準備啟動另一個正在恢復的activity時調用。這個方法通常用于把未保存的改動提交為永久數據、停止動畫播放、以及其它可能消耗CPU的工作等等。 它應該非常迅速地完成工作,因為下一個activity在本方法返回前是不會被恢復運行的。 如果activity返回前臺,則下一個回調方法是onResume();如果進入用戶不可見狀態,則下一個是onStop() | 可以 | onResume() 或 onStop() |
| onStop() | 當activity不再對用戶可見時調用。原因可能是它即將被銷毀、或者其它activity(已有或新建的)被恢復運行并要覆蓋本activity。 如果activity還會回來與用戶交互,則下一個回調方法是onRestart();如果這個activity即將消失,則下一個回調方法是onDestroy() | 可以 | onRestart() 或 onDestroy() |
| onDestroy() | 在本activity被銷毀前調用。這是activity收到的最后一個調用。 可能是因為activity完成了工作(有些人在這里調用finish()), 也可能是因為系統為了騰出空間而臨時銷毀activity的本實例。 可以利用isFinishing() 方法來區分這兩種情況。 | 可以 | 無 |
標為“之后可否被殺死?”的列指明了系統是否可以在這個方法返回之后的任意時刻殺掉這個activity的宿主進程, 而不再執行其它流程上的activity代碼。 有三個方法是標為“可以”:( onPause()、 onStop()、 和onDestroy())。 因為onPause()是三個方法中的第一個, 一旦activity被創建, onPause() 就是進程可以被殺死之前最后一個能確保被調用的方法 ——如果系統在某種緊急情況下必須回收內存,則 onStop() 和onDestroy() 可能就不會被調用了。因此,你應該使用 onPause() 來把至關重要的需長期保存的數據寫入存儲器(比如用戶所編輯的內容)。 不過,應該對必須通過 onPause() 方法進行保存的信息有所選擇,因為該方法中所有的阻塞操作都會讓切換到下一個activity的停滯,并使用戶感覺到遲緩。
“之后可否被殺死?”列中標為“否”的方法,在它們被調用時的那一刻起,就會保護本activity的宿主進程不被殺掉。 因此,只有在 onPause() 方法返回時至 onResume() 方法被調用時之間,activity才會被殺掉。直到 onPause() 再次被調用并返回時,activity都不會再次允許被殺死。
Note:表1中標明的技術上不“可殺”的activity仍然有可能會被系統殺死——但那只有在沒有任何資源的極端情況下才會發生。 什么時候activity會被殺掉,已在文檔進程和線程里詳細說明了。
保存activity的狀態
管理Activity生命周期一節中已簡單提到,當一個activity被paused或者stopped時,activity的狀態可以被保存。 的確如此,因為 Activity 對象在paused或者stopped時仍然被保留在內存之中——它所有的成員信息和當前狀態都仍然存活。 這樣用戶在activity里所作的改動全都還保存著,所以當activity返回到前臺時(當它“resume“),那些改動仍然有效。
不過,如果系統是為了回收內存而銷毀activity,則這個 Activity 對象就會被銷毀,這樣系統就無法簡單地resume一下就能還原完整狀態的activity。 如果用戶要返回到這個activity的話,系統必須重新創建這個Activity 對象。可是用戶并不知道系統是先銷毀activity再重新創建了它的,所以,他很可能希望activity完全保持原樣。 這種情況下,你可以保證activity狀態的相關重要信息都由另一個回調方法保存下來了,此方法讓你能保存activity狀態的相關信息: onSaveInstanceState()。
在activity變得很容易被銷毀之前,系統會調用 onSaveInstanceState()方法。 調用時系統會傳入一個Bundle對象, 你可以利用 putString() 之類的方法,以鍵值對的方式來把activity狀態信息保存到該Bundle對象中。 然后,如果系統殺掉了你的application進程并且用戶又返回到你的activity,系統就會重建activity并將這個 Bundle 傳入onCreate() 和onRestoreInstanceState() 中,你就可以從 Bundle 中解析出已保存信息并恢復activity狀態。如果沒有儲存狀態信息,那么傳入的 Bundle 將為null(當activity第一次被創建時就是如此)。
注意:?activity被銷毀之前,并不能確保每次都會調用 onSaveInstanceState() ,因為存在那些不必保存狀態的情況(比如用戶使用BACK鍵離開了你的activity,因為用戶明顯是關了這個activity)。 如果系統要調用 onSaveInstanceState() 方法,那么它通常會在 onStop() 方法之前并且可能是在 onPause() 之前調用。
不過,即使你沒有實現 onSaveInstanceState() 方法,有些activity狀態還是會通過 Activity 類默認實現的onSaveInstanceState() 方法保存下來。特別的是,默認為layout中的每個 View 實現了調用相應的onSaveInstanceState() 方法,這允許每一個view提供自己需被保存的信息。 幾乎Android框架下所有的widget都會在適當的時候實現該方法,這樣,任何UI上可見的變化都會自動保存下來,并在activity重建后自動恢復。 例如,EditText widget會保存所有用戶已經輸入的文本, CheckBoxwidget 也會保存是否被選中。你所要做的工作僅僅是為每一個你想要保存其狀態的widget提供一個唯一的ID(就是 android:id 屬性)。如果這個widget沒有ID的話,系統是無法保存它們的狀態的。
通過把android:saveEnabled 設置為"false",或者調用 setSaveEnabled() 方法,你也可以顯式地阻止layout中的某個view保存狀態。 通常不應該禁用保存,不過假如你需要恢復activity UI的各個不同的狀態,也許可以這么做。
盡管缺省實現的 onSaveInstanceState() 方法會保存activity UI的有用信息,你仍然需要覆蓋它來存入更多的信息。 例如,你可能需要保存在activity生命周期中改變的成員變量值(可能是關于UI恢復的值,但是默認情況下,存放這些UI狀態的成員變量值是不會被恢復的)。
因為默認實現的 onSaveInstanceState() 方法已經幫你保存了一些UI的狀態,所以如果你重寫此方法是為了保存更多的狀態信息,那么在執行自己的代碼之前應該確保先調用一次父類的 onSaveInstanceState() 方法。同理,如果重寫 onRestoreInstanceState() 的話,也應該調用一次父類的該方法,這樣缺省的代碼就能正常恢復view的狀態了。
注意:因為 onSaveInstanceState() 并不保證每次都會被調用,所以你應該只用它來記錄activity的一些臨時狀態信息(UI的狀態)——千萬不要用它來保存那些需要長久保存的數據。 替代方案是,你應該在用戶離開activity的時候利用 onPause() 來保存永久性數據(比如那些需要存入數據庫里的數據)。
配置改動后的處理
設備的某些配置可能會在運行時發生變化(比如屏幕方向、鍵盤可用性以及語言)。 當發生這些變化時,Android會重建這個運行中的activity(系統會調用 onDestroy() ,然后再馬上調用 onCreate() )。這種設計有助于應用程序適用新的參數配置,通過把你預置的可替換資源(比如對應各種屏幕方向和尺寸的layout)自動重新裝載進入應用程序的方式來實現。
如果你采取了適當的設計,讓activity能夠正確地處理這些因為屏幕方向而引起的重啟,并能如上所述地恢復activity狀態, 那么你的應用程序將對生命周期中其它的意外事件更具適應能力。
處理這類重啟的最佳方式,就是利用 onSaveInstanceState() 和onRestoreInstanceState() (或者 onCreate() )進行狀態的保存和恢復。
多activity的合作
當activity啟動另一個activity時,它倆生命周期的狀態都會發生轉換。 第一個activity paused并stopped(盡管它也可能不會被stopped,如果它仍然后臺可見的話),而另一個activity是被created。 如果這兩個activity共用了保存在磁盤或其它地方的數據,那么請明白:在第二個activity被created之前,第一個activity還沒有完全被stopped,這點非常重要。第二個activity的啟動進程與第一個activity的關閉進程在時間上會發生重疊。
生命周期回調方法的順序是很明確的,特別是兩個activity位于同一個進程中、一個啟動另一個的時候。 下面就是Aactivity A啟動Activity B時的操作順序:
Activity A的 onPause()方法,如果活動后臺不可見的話,運行onStop(),否則不運行。
Activity B的 onCreate() ,onStart() 和onResume() 方法依次運行。(Activity B現在獲得用戶焦點。)
轉載于:https://my.oschina.net/JiangTun/blog/639976
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的Android学习笔记系列四2 —— Activity的生命周期的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UIImage使用
- 下一篇: Android官方开发文档Trainin