1. 程式人生 > >Android基礎:Activity生命週期細化

Android基礎:Activity生命週期細化

一、   細化Activity的生命週期

        在進行Android應用開發的時候,需要考慮如何使用Activity的生命週期中的方法使得程式符合使用者的期望且在activity不需要的時候不會導致系統資源的浪費。下面從activity的啟動和銷燬、暫停和恢復、停止和重啟、重新建立等方面展開介紹。

        1、啟動和銷燬Activity

        (1)理解生命週期的回撥

        1) 在一個activity的生命週期中,系統會像金字塔模型一樣去呼叫一系列的生命週期回撥方法。Activity生命週期的每一個階段就像金字塔的臺階。當系統建立了一個新的activity例項,每一個回撥函式會向上一階移動activity狀態。金字塔頂端意味著activity是跑在最前端的並且使用者可以與它進行互動。

        2) 當用戶開始離開這個activity,為了解除安裝這個activity,系統會呼叫其他方法來向下一階移動activity狀態。在某些情況下,activity會隱藏在金字塔下等待(例如當用戶切換到其他app),此時activity可以重新回到頂端(如果使用者回到這個activity)並且恢復使用者離開時的狀態。

        上圖所示的生命狀態中,有三個狀態是靜態的,在這三個狀態下activity可以存在一段比較長的時間。(其它幾個狀態會很快就切換掉,停留的時間比較短暫)

        1)Resumed

        通常被理解為“running”狀態,在該狀態時,activity是在最前端的,使用者可以與它進行互動。

        2)Paused

        這個狀態下,activity會被另一個activity所遮蓋:另外的activity來到最前面,但是半透明的,不會覆蓋整個螢幕。被覆蓋的activity不會再接受使用者的輸入且不會執行任何程式碼。(這裡的不執行任何程式碼並不代表任何後臺執行緒都不會工作)

        3)Stopped

        這個狀態下,activity完全被隱藏,不被使用者可見,可以認為是在後臺。在stopped狀態時,activity例項與它的所有狀態資訊都會被保留,但是activity不能執行任何程式碼。

        4)其他狀態(Created與Started)都是短暫的,系統快速地執行那些回撥函式並通過執行下一階段的回撥函式移動到下一個狀態。也就是說,在系統呼叫onCreate(),之後會迅速呼叫onStart(),之後再迅速執行onResume()。

        (2)建立一個新的例項

        大多數app都包括許多不同的activities,這樣使得使用者可以執行不同的動作。不論這個activity是建立的主activtiy還是為了響應使用者行為而新建立的,系統都會呼叫新的activity例項中的onCreate()方法。

        你必須實現onCreate()方法來執行程式啟動所需要的基本邏輯。

        onCreate()裡面儘量少做事情,避免程式啟動太久都看不到介面。

        一旦結束onCreate()操作,系統會迅速呼叫onStart()與onResume()方法。你的activity不會在Created或者Started狀態停留。技術上來說,activity在onStart()被呼叫後開始被使用者可見,但是onResume()會迅速被執行使得activity停留在Resumed狀態,直到一些因素髮生變化才會改變這個狀態。例如接到一個來電,使用者切換到另一個activity,或裝置螢幕關閉。

        onCreate()方法包含了一個引數叫做saveInstanceState,在下面介紹重新建立activity時涉及。

        上圖顯示了onCreate(),onStart(),onResume()是如何執行的。當這三個順序執行的回撥函式完成後,activity會到達Resumed狀態。

        (3)銷燬Activity

        大多數apps並不需要實現這個方法,因為區域性類的references會隨著activity的銷燬而銷燬,並且你的activity應該在onPause()與onStop()中執行清除activity資源的操作。然而,如果你的activity包含了你在onCreate時建立的後臺執行緒,或者是其他有可能導致記憶體洩露的資源,你應該在onDestroy()時殺死它們。

        系統通常是在執行了onPause()與onStop() 之後再呼叫onDestroy() ,除非你的程式在onCreate()方法裡面就呼叫了finish()方法。在某些情況下,例如你的activity只是做了一個臨時的邏輯跳轉的功能,它只是用來決定跳轉到哪一個activity,這樣的話,你需要在onCreate()裡面去呼叫finish()方法,這樣系統會直接就呼叫onDestory()方法,其它生命週期的方法則不會被執行。

        2、暫停和恢復Activity

        在使用app時,前端的activity有時候會被其他可見的元件而阻塞(obstructed),這樣會導致當前的activity進入到Paused狀態。例如,當開啟一個半透明的activity時(對話方塊),之前的activity會被暫停,只要這個activity仍然被部分可見,之前的activity則一直處於Paused狀態。

        然而,一旦之前的activity被完全阻塞並不可見,它則會進入Stopped狀態。

        當你的activity進入Paused狀態,系統會呼叫onPause()方法,在這個方法裡面可以允許你執行停止目前正在執行任務的操作,比如暫停視訊播放或者是儲存那些有可能需要長期儲存的資訊。如果使用者從暫停狀態回到你的activity,系統應該恢復那些資料並執行onResume()方法。

        當你的activity呼叫onPause()方法,那可能意味者activity將被暫停一段時間,並且使用者很可能回到你的activity。然而,那也是使用者要離開你的activtiy的第一個訊號。

        上圖顯示了,當一個半透明的activity阻塞你的activity時,系統會呼叫①onPause()方法並且這個activity會停留在Paused狀態。如果使用者在這個activity處在Paused狀態時回到這個activity,系統則會呼叫它的②onResume()方法。

        (1)暫停你的Activity

        當系統呼叫你的activity中的onPause(),從技術上講,那意味著你的activity仍然處於部分可見的狀態,但大多數時候,那意味著使用者正在離開這個activity並馬上會進入Stopped狀態.你通常應該在onPause()回撥方法裡面做下面的事情:

        停止動畫或者是其他正在執行的操作,那些都會導致CPU的浪費。

        提交沒有儲存的改變,但是僅僅是在使用者離開時期待儲存的內容(例如郵件草稿。

        釋放系統資源,例如broadcastreceivers,sensors (比如GPS),Camera或者是其他任何會影響到電量的資源。

        通常,不應該在使用onPause()方法時來儲存使用者改變的資料(例如填入表格中的個人資訊)到永久儲存(File或者DataBase)上。僅僅當你確認使用者期待那些改變能夠被自動儲存的資訊的時候(例如正在撰寫郵件草稿),你可以把那些資料存到永久儲存。然而,你應該避免在onPause()時執行CPU-intensive的工作,例如寫資料到DB,因為它會導致切換到下一個activity變得緩慢(應該把那些heavy-load的工作放到onStop()方法中去做)。

        如果你的activity實際上是要被Stop,那麼為了切換的順暢應該減少在OnPause()方法中的工作量。

        當你的activity處於暫停狀態,Activity例項是駐留在記憶體中的,並且在activity恢復的時候重新呼叫。不需要在恢復到Resumed狀態的一系列回撥方法中重新初始化元件。

        (2)恢復你的Activity

        當用戶從Paused狀態恢復你的activity時,系統會呼叫onResume()方法。系統每次呼叫這個方法,activity都處於最前臺,包括第一次建立的時候。所以應該在onResume()方法中來初始化那些你在onPause()方法中釋放掉的元件,並執行那些activity每次進入Resumed狀態都需要的初始化動作(例如開始動畫與初始化那些只有在獲取使用者焦點時才需要的元件)。

        3、停止和重啟Activity

        恰當的停止與重啟你的activity是很重要的,在activity生命週期中,他們能確保使用者感知到程式的存在並不會丟失他們的進度。在下面一些關鍵的場景中會涉及到停止與重啟:

        使用者開啟最近使用app的選單並切換你的app到另外一個app,這個時候你的app是被停止的。如果使用者通過手機主介面的啟動程式圖示或者最近使用程式的視窗回到你的app,那麼你的activity會重啟。

        使用者在你的app裡面執行啟動一個新的activity的操作,當前activity會在第二個activity被建立後stop。如果使用者點選back按鈕,第一個activtiy會被重啟。

        使用者在使用你的app時接受到一個來電通話。

        Activity類提供了onStop()與onRestart()方法來允許在activity停止與重啟時進行呼叫。不同於暫停狀態是部分阻塞UI,停止狀態是UI不再可見並且使用者的焦點轉移到另一個activity中。

        因為系統在activity停止時會在記憶體中儲存Activity例項。有些時候不需要實現onStop(),onRestart()甚至是onStart()方法。因為大多數的activity相對比較簡單,activity會自己停止與重啟,你只需要使用onPause()方法來停止正在執行的動作並斷開系統資源連線。

        上圖顯示:當用戶離開你的activity,系統會呼叫①onStop()方法來停止activity;這個時候如果使用者返回,系統會呼叫②onRestart()方法,之後會迅速呼叫③onStart()方法與④onResume()方法。需要注意的是:無論什麼原因導致activity停止,系統總是會在onStop()方法之前呼叫onPause()方法。

        (1)停止你的Activity

        當你的activity呼叫onStop()方法,activity不再可見,並且應該釋放那些不再需要的所有資源。一旦你的activity停止了,系統會在不再需要這個activity時摧毀它的例項(和棧結構有關,通常back操作會導致前一個activity被銷燬)。在極端情況下,系統會直接殺死你的app程序,並且不執行activity的onDestroy()回撥方法,因此你需要使用onStop()來釋放資源,從而避免記憶體洩漏。(這點需要注意)

        儘管onPause()方法是在onStop()之前呼叫,你應該使用onStop()來執行那些CPU-intensive的shut-down操作,例如往資料庫中寫入資料。

        當你的activity已經停止,Activity物件會儲存在記憶體中,並且在activityresume的時候重新被呼叫到。你不需要在恢復到Resumed狀態前重新初始化那些被儲存在記憶體中的元件。系統同樣儲存了每一個在佈局中的檢視的當前狀態,如果使用者在EditText元件中輸入了text,它會被儲存,因此不需要儲存與恢復它。

        即使系統會在activity stop的時候銷燬這個activity,它仍然會儲存View物件的狀態(比如EditText中的文字)到一個Bundle中,並且在使用者返回這個activity時恢復他們(下面會介紹在activity銷燬與重新建立時如何使用Bundle來儲存其他資料的狀態)。

        (2)啟動與重啟你的Activity

        當你的activity從Stopped狀態回到前臺時,它會先呼叫onRestart()方法,再呼叫onStart()方法;onStart()方法會在每次你的activity可見時都會被呼叫。onRestart()方法則是隻在activity從Stopped狀態恢復時才會被呼叫,因此你可以使用它來執行一些特殊的恢復(restoration)工作。注意這裡之前是被Stopped而不是destroy。

        使用onRestart()方法來恢復activity狀態是不太常見的,因此對於這個方法如何使用沒有任何的原則(guidelines)。然而,因為你的onStop()方法應該做清除所有activity資源的操作,你需要在重新啟動activtiy時重新例項化那些被清除的資源,同樣,你也需要在activity第一次建立時例項化那些資源。介於上面的原因,你應該使用onStart()作為onStop()所對應方法。因為系統會在建立activity與從停止狀態重啟activity時都會呼叫onStart()。(意思是說你在onStop裡面做了哪些清除的操作就應該在onStart裡面重新把那些清除掉的資源重新創建出來)。

        例如:因為使用者很可能在回到這個activity之前需要過一段時間,所以onStart()方法是一個比較好的地方來驗證某些必須的系統特性是否可用,如GPS是否可用。

        當系統Destory你的activity,它會為你的activity呼叫onDestroy()方法。因為我們會在onStop方法裡面做釋放資源的操作,那麼onDestory方法則是你最後去清除那些可能導致記憶體洩漏的地方。因此你需要確保那些執行緒都被destroyed並且所有的操作都被停止。

        4、重新建立Activity

        當你的Activity是因為使用者點選Back按鈕或者是activity通過呼叫finish()結束自己時,系統就丟失了Activity例項這個概念,因為前面的行為意味著不再需要這個activity了。然而,如果因為系統資源緊張而導致Activity的Destory, 系統會在使用者回到這個Activity時有這個Activity存在過的記錄,系統會使用那些儲存的記錄資料(描述了當Activity被Destory時的狀態)來重新建立一個新的Activity例項。那些被系統用來恢復之前狀態而儲存的資料被叫做"instance state" ,它是一些存放在Bundle物件中的鍵值對(key-valuepairs)。

        你的Activity會在每次旋轉螢幕時被destroyed與recreated。當螢幕改變方向時,系統會Destroy與Recreate前臺的activity,因為螢幕配置被改變,你的Activity可能需要載入一些alternative的資源(例如layout)。

        預設情況下,系統使用Bundle 例項來儲存每一個檢視物件中的資訊(例如輸入EditText中的文字內容)。因此,如果你的Activity被destroyed與recreated,那麼layout的狀態資訊會自動恢復到之前的狀態。然而,你的activity也許存在更多你想要恢復的狀態資訊,例如記錄使用者Progress的成員變數(membervariables)。

        為了讓你可以儲存額外更多的資料到savedinstance state。在Activity的宣告週期裡面存在一個新增的回撥函式onSaveInstanceState(),你必須重寫這個函式。當用戶離開你的Activity時,系統會呼叫它。當系統呼叫這個函式時,系統會在你的Activity被異常Destory時傳遞Bundle 物件,這樣你可以增加額外的資訊到Bundle中並儲存與系統中。然後如果系統在Activity被Destory之後想重新建立這個Activity例項時,之前的那個Bundle物件會被傳遞到你的activity的onRestoreInstanceState()方法與onCreate() 方法中。

        當系統開始停止你的Activity時,只有在Activity例項會需要重新建立的情況下才會呼叫到①onSaveInstanceState(),在這個方法裡面可以指定額外的狀態資料到Bundle中。如果這個Activity被destroyed然後這個例項又需要被重新建立時,系統會傳遞在①中的狀態資料到②onCreate()與③onRestoreInstanceState()。

        通常來說,跳轉到其他的activity或者是點選Home鍵都會導致當前的activity執行onSaveInstanceState()方法,因為這種情況下的activity都是有可能會被destroy並且是需要儲存狀態以便後續恢復使用的;而從跳轉的activity點選back回到前一個activity,那麼跳轉前的activity是執行退棧的操作,所以這種情況下是不會執行onSaveInstanceState()方法的,因為這個activity不可能存在需要重建的操作。

        (1)儲存Activity狀態

        當你的activity開始Stop,系統會呼叫onSaveInstanceState()方法,因此你的Activity可以用鍵值對的集合來儲存狀態資訊。這個方法會預設儲存Activity檢視的狀態資訊,例如在EditText 元件中的文字或者是ListView的滑動位置。

        為了給Activity儲存額外的狀態資訊,你必須實現onSaveInstanceState()方法 並增加key-valuepairs到Bundle 物件中。

        總是需要呼叫onSaveInstanceState() 方法的父類實現,這樣預設的父類實現才能儲存檢視狀態的資訊。

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

        (2)恢復Activity狀態

        當你的Activity從Destory中重建,你可以從系統傳遞給你的Activity的Bundle中恢復儲存的狀態。onCreate()與 onRestoreInstanceState() 回撥方法都接收到了同樣的Bundle,裡面包含了同樣的例項狀態資訊。

        因為onCreate() 方法會在第一次建立新的Activity例項與重新建立之前被Destory的例項時都被呼叫,你必須在你嘗試讀取Bundle 物件前Check它是否為null。如果它為null,系統則是建立一個新的Activityinstance,而不是恢復之前被Destory的Activity。

        你也可以選擇實現onRestoreInstanceState() ,而是不是在onCreate方法裡面恢復資料。onRestoreInstanceState()方法會在onStart() 方法之後執行.系統僅僅會在存在需要恢復的狀態資訊時才會呼叫onRestoreInstanceState() ,因此你不需要檢查Bundle 是否為null。

        與上面儲存一樣,總是需要呼叫onRestoreInstanceState()方法的父類實現,這樣預設的父類實現才能儲存檢視狀態的資訊。

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