Android開發者指南(4) —— Application Fundamentals(二)

線程安全方法(Thread-safe methods)html

         在一些狀況下,你所實現的方法有可能會被多於一個的線程所調用,因此它們必須被寫成線程安全的。java

         對於咱們上一節所討論的RPC機制中的能夠被遠程調用的方法來講,這是必須首先考慮的。若是針對一個IBinder對象中實現的方法的調用源自這個 IBinder對象所在的進程時,這個方法將會在調用者的線程中執行。然而,若是這個調用源自其它的進程,則這個方法將會在一個線程池中選出的線程中運 行,這個線程池由Android加以管理,並與IBinder存在於同一進程內;這個方法不會在進程的主線程內執行。反過來講,一個服務的 onBind() 方法應爲服務進程的主線程所調用,而實現了由 onBind() 返回的對象(好比說,一個實現了RPC方法的Stub的子類)的方法將爲池中的線程所調用。由於服務能夠擁有多於一個的客戶端,而同一時間,也會有多個池中的線程調用同一個IBinder方法。所以IBinder方法必須實現爲線程安全的。android

         相似的,一個內容提供者能接受源自其它進程的請求數據。儘管ContentResolverContentProvider類隱藏了交互溝經過程的管理細節,ContentProvider會由query() insert() delete() update()getType()方法來相應這些請求,而這些方法也都是由那個內容提供者的進程中所包涵的線程池提供的,而不是進程的主線程自己。因此這些有可能在同一時間被不少線程調用的方法也必須被實現爲線程安全的。緩存

 

組件生命週期(Component Lifecycles)安全

應用程序組件有其生命週期──Android初始化它們以相應intent直到這個實例被摧毀。在此之間,它們有時是激活的有時則相反。或者,若是它是 一個activity,則是可爲用戶所見或者不能。這一節討論了activity、服務以及廣播接收器的生命週期,包括它們在生命週期中的狀態、在狀態之 轉變時通知你的方法、以及當這些進程被關閉或實例被摧毀時,這些狀態產生的效果。網絡

 

  Activity生命週期(Activity lifecycle)多線程

         一個activity主要有三個狀態:app

 * 當在屏幕前臺時(位於當前任務堆棧的頂部),它是活躍運行的狀態。它就是相應用戶操做的activityide

 * 當它失去焦點但仍然對用戶可見時,它處於暫停狀態。便是:在它之上有另一個activity。這個activity也許是透明的,或者未能徹底遮蔽全屏,因此被暫停的activity仍對用戶可見。暫停的activity仍然是存活狀態(它保留着全部的狀態和成員信息並鏈接至窗口管理器),但當系統處於極低內存的狀況下,仍然能夠殺死這個activitypost

 * 若是它徹底被另外一個activity覆蓋是,它處於中止狀態。它仍然保留全部的狀態和成員信息。然而它不在爲用戶可見,因此它的窗口將被隱藏,若是其它地方須要內存,則系統常常會殺死這個activity

若是一個activity處於暫停或中止狀態,系統能夠經過要求它結束(調用它的 finish() 方法)或直接殺死它的進程來將它驅出內存。當它再次爲用戶可見的時候,它只能徹底從新啓動並恢復至之前的狀態。

當一個activity從這個狀態轉變到另外一個狀態時,它被如下列protected方法所通知:

      void onCreate(Bundle savedInstanceState)

void onStart()

void onRestart()

void onResume()

void onPause()

void onStop()

void onDestroy()

         你能夠重載全部這些方法以在狀態改變時進行合適的工做。全部的activity都必須實現 onCreate() 用以當對象第一次實例化時進行初始化設置。不少activity會實現 onPause()以提交數據變化或準備中止與用戶的交互。

 

  調用父類(Calling into the superclass)

         全部activity生命週期方法的實現都必須先調用其父類的版本。好比說:

        

         總得來講,這七個方法定義了一個activity完整的生命週期。實現這些方法能夠幫助你監察三個嵌套的生命週期循環:

 * 一個activity 完整的生命週期 自第一次調用 onCreate()開始,直至調用onDestroy()爲止。activityonCreate()中設置全部全局狀態以完成初始化,而在onDestroy()中釋放全部系統資源。好比說,若是activity有一個線程在後臺運行以從網絡上下載數據,它會以 onCreate()建立那個線程,而以 onDestroy()銷燬那個線程。

 * 一個activity 可視生命週期 onStart() 調用開始直到相應的 onStop()調用。在此期間,用戶能夠在屏幕上看到此activity,儘管它也許並非位於前臺或者正在與用戶作交互。在這兩個方法中,你能夠管控用來向用戶顯示這個activity的資源。好比說,你能夠在onStart() 中註冊一個BroadcastReceiver 來監控會影響到你UI的改變,而在onStop() 中來取消註冊,這時用戶是沒法看到你的程序顯示的內容的。onStart() onStop() 方法能夠隨着應用程序是否爲用戶可見而被屢次調用。

 * 一個activity 前臺生命週期 onResume() 調用起,至相應的 onPause()調用爲止。在此期間,activity位於前臺最上面並與用戶進行交互。activity會常常在暫停和恢復之間進行狀態轉換──好比說當設備轉入休眠狀態或有新的activity啓動時,將調用onPause() 方法。當activity得到結果或者接收到新的intent的時候會調用onResume() 方法。所以,在這兩個方法中的代碼應當是輕量級的。

下圖展現了上述循環過程以及activity在這個過程之中歷經的狀態改變。着色的橢圓是activity能夠經歷的主要狀態。矩形框表明了當activity在狀態間發生改變的時候,你進行操做所要實現的回調方法。

 

 

下表詳細描述了這些方法,並在activity的整個生命週期中定位了它們。

方法

描述

是否可被殺死(Killable?)

下一個

onCreate()

activity第一次被建立的時候調用。這裏是你作全部初始化設置的地方──建立視圖、綁定數據至列表等。若是曾經有狀態記錄(參閱後述Saving Activity State。),則調用此方法時會傳入一個包含着此activity之前狀態的包對象作爲參數。

接下來始終遵循調用onStart()

onStart()

onRestart()

activity中止後,在再次啓動以前被調用。

接下來始終遵循調用onStart()

onStart()

onStart()

activity正要變得爲用戶所見時被調用。

activity轉向前臺時接下來調用onResume(),在activity變爲隱藏時接下來調用onStop()

onResume()

onStop()

onResume()

activity開始與用戶進行交互以前被調用。此時activity位於堆棧頂部,並接受用戶輸入。

接下來始終遵循調用onPause()

onPause()

onPause()

當系統將要啓動另外一個activity時調用。此方法主要用來將未保存的變化進行持久化,中止相似動畫這樣耗費CPU的動做等。這一切動做應該在短期內完成,由於下一個activity必須等到此方法返回後纔會繼續。

activity從新回到前臺時接下來調用onResume()。當activity變爲用戶不可見時接下來調用onStop()

onResume()

onStop()

onStop()

activity再也不爲用戶可見時調用此方法。這可能發生在它被銷燬或者另外一個activity(多是現存的或者是新的)回到運行狀態並覆蓋了它。

若是activity再次回到前臺跟用戶交互則接下來調用onRestart(),若是關閉activity則接下來調用onDestroy()

onRestart()

or

onDestroy()

onDestroy()

activity銷燬前調用。這是activity接收的最後一個調用。這可能發生在activity結束(調用了它的 finish() 方法)或者由於系統須要空間因此臨時的銷燬了此acitivity的實例時。你能夠用isFinishing() 方法來區分這兩種狀況。

         請注意上表中可被殺死一列。它標示了在方法返回後,還沒執行activity的其他代碼的任意時間裏,系統是否能夠殺死包含此activity的進程。三個方法(onPause() onStop()onDestroy())被標記爲onPause()是三個中的第一個,它也是惟一一個在進程被殺死以前必然會調用的方法──onStop() onDestroy() 有可能不被執行。所以你應該用 onPause() 來將全部持久性數據(好比用戶的編輯結果)寫入存儲之中。

         可被殺死一列中標記爲的方法在它們被調用時將保護activity所在的進程不會被殺死。因此只有在onPause()方法返回後到onResume() 方法被調用時,一個activity才處於可被殺死的狀態。在onPause()再次被調用並返回以前,它不會被系統殺死。

    如後面一節進程和生命週期所述,即便是在這裏技術上沒有被定義爲可殺死activity仍然有可能被系統殺死──但這僅會發生在實在沒有其它方法的極端狀況之下。

 

  保存activity狀態(Saving activity state)

         當系統而不是用戶本身出於回收內存的考慮,關閉了一個activity以後。用戶會指望當他再次回到那個activity的時候,它仍保持着上次離開時的樣子。

         爲了獲取activity被殺死前的狀態,你應該爲activity實現onSaveInstanceState() 方法。Androidactivity有可能被銷燬以前(即onPause() 調用以前)會調用此方法。它會將一個以名稱-值對方式記錄了activity動態狀態的Bundle 對象傳遞給該方法。當activity再次啓動時,這個Bundle會傳遞給onCreate()方法和隨着onStart()方法調用的onRestoreInstanceState(),因此它們兩個均可以恢復捕獲的狀態。

         onPause()或先前討論的其它方法不一樣,onSaveInstanceState() onRestoreInstanceState() 並非生命週期方法。它們並非總會被調用。好比說,Android會在activity易於被系統銷燬以前調用 onSaveInstanceState(),但用戶動做(好比按下了BACK鍵)形成的銷燬則不調用。在這種狀況下,用戶沒打算再次回到這個activity,因此沒有保存狀態的必要。

         由於onSaveInstanceState()不是總被調用,因此你應該只用它來爲activity保存一些臨時的狀態,而不能用來保存持久性數據。而是應該用onPause()來達到這個目的。

 

  服務生命週期(Coordinating activities)

         服務以兩種方式使用:

 * 它能夠啓動並運行,直至有人中止了它或它本身中止。在這種方式下,它以調用Context.startService()啓動,而以調用Context.stopService()結束。它能夠調用Service.stopSelf() Service.stopSelfResult()來本身中止。不論調用了多少次startService()方法,你只須要調用一次stopService()來中止服務。

 * 它能夠經過本身定義並暴露出來的接口進行程序操做。客戶端創建一個到服務對象的鏈接,並經過那個鏈接來調用服務。鏈接以調用Context.bindService()方法創建,以調用 Context.unbindService()關閉。多個客戶端能夠綁定至同一個服務。若是服務此時尚未加載,bindService()會先加載它。

這兩種模式並非徹底分離的。你能夠綁定至一個用 startService()啓動的服務。好比說,一個後臺音樂播放服務能夠調用startService()並傳遞給它一個包含欲播放的音樂列表的Intent對象來啓動。不久,當用戶想要對播放器進行控制或者查看當前播放曲目的詳情時,會啓用一個activity,調用bindService()鏈接到服務來完成操做。在這種狀況下,直到綁定鏈接關閉stopService() 纔會真正中止一個服務。

activity同樣,服務也有一系列你能夠實現以用於監控其狀態變化的生命週期方法。但相對於activity要少一些,只有三個,並且,它們是public屬性,並不是protected

void onCreate()

void onStart(Intent intent)

void onDestroy()

         倚仗實現這些方法,你監控服務的兩個嵌套的生命週期循環:

 * 服務的完整生命週期始於調用onCreate()而終於onDestroy()方法返回。如同activity同樣,服務在onCreate()裏面進行它本身的初始化,而在onDestroy()裏面釋放全部資源。好比說,一個音樂回放服務能夠在onCreate()中建立播放音樂的線程, 而在onDestroy()中中止這個線程。

 * 服務的活躍生命週期始於調用onStart()。這個方法用於處理傳遞給startService()Intent對象。音樂服務會打開Intent來探明將要播放哪首音樂,並開始播放。

服務中止時沒有相應的回調方法──不存在onStop()方法。

         onCreate()onDestroy()方法在全部服務中都會被調用,不管它們是由Context.startService()仍是由Context.bindService()所啓動的。而onStart()僅會被startService()所啓用的服務調用。

         若是一個服務容許別的進程綁定,則它還會有如下額外的回調方法以供實現:

IBinder onBind(Intent intent)

boolean onUnbind(Intent intent)

void onRebind(Intent intent)

         傳遞給bindServiceIntent的對象也會傳遞給onBind()回調方法,而傳遞給unbindService()Intent對象一樣傳遞給onUnbind()。若是服務容許綁定,onBind()將返回一個供客戶端與服務進行交互的通信渠道。若是有新的客戶端鏈接至服務,則onUnbind()方法能夠要求調用onRebind()

         下圖描繪了服務的回調方法。儘管圖中對由startService startService方法啓動的服務作了區分,但要記住,不論一個服務是怎麼啓動的,它均可能容許客戶端的鏈接,因此任何服務均可以接受onBind()onUnbind()調用。

 

 

廣播接收器生命週期(Broadcast receiver lifecycle)

         廣播接收器只有一個回調方法:

                  void onReceive(Context curContext, Intent broadcastMsg)

         當廣播消息抵達接收器時,Android調用它的onReceive() 方法並將包含消息的Intent對象傳遞給它。廣播接收器僅在它執行這個方法時處於活躍狀態。當onReceive()返回後,它即爲失活狀態。

         擁有一個活躍狀態的廣播接收器的進程被保護起來而不會被殺死。但僅擁有失活狀態組件的進程則會在其它進程須要它所佔有的內存的時候隨時被殺掉。

         這種方式引出了一個問題:若是響應一個廣播信息須要很長的一段時間,咱們通常會將其歸入一個衍生的線程中去完成,而不是在主線程內完成它,從而保證用戶交互過程的流暢。若是onReceive()衍生了一個線程而且返回,則包涵新線程在內的整個進程都被會判爲失活狀態(除非進程內的其它應用程序組件仍處於活躍狀態),因而它就有可能被殺掉。這個問題的解決方法是令onReceive()啓動一個新服務,並用其完成任務,因而系統就會知道進程中仍然在處理着工做。

         下一節中,咱們會討論更多進程易誤殺的問題。

 

進程與生命週期(Processes and lifecycles)

         Android系統會盡量長的延續一個應用程序進程,但在內存太低的時候,仍然會不可避免須要移除舊的進程。爲決定保留或移除一個進程,Android 將每一個進程都放入一個重要性層次中,依據則是它其中運行着的組件及其狀態。重要性最低的進程首先被消滅,而後是較低的,依此類推。重要性共分五層,依 據重要性列表以下:

1.         前臺進程是用戶操做所必須的。當知足以下任一條件時,進程被認爲是處於前臺的:

* 它運行着正在與用戶交互的activityActivity對象的 onResume() 方法已被調用)。

* 一個正在與用戶交互的activity使用着它提供的一個服務。

* 它包含着一個正在執行生命週期回調方法(onCreate()onStart()onDestroy())的Service對象。

* 它包含着一個正在執行 onReceive() 方法的BroadcastReceiver對象。

任一時間下,僅有少數進程會處於前臺,僅當內存實在沒法供給它們維持同時運行時纔會被殺死。通常來講,在這種狀況下,設備已然處於使用虛擬內存的狀態,必需要殺死一些前臺進程以用戶界面保持響應。

2.         可視進程沒有前臺組件,但仍可被用戶在屏幕上所見。當知足以下任一條件時,進程被認爲是可視的:

* 它包含着一個不在前臺,但仍然爲用戶可見的activity(它的onPause()方法被調用)。這種狀況可能出如今如下狀況:好比說,前臺activity是一個對話框,而以前的activity位於其下並能夠看到。

* 它包含了一個綁定至一個可視的activity的服務。

可視進程依然被視爲是很重要的,非到不殺死它們便沒法維持前臺進程運行時,纔會被殺死。

3.         服務進程是由 startService() 方法啓動的服務,它不會變成上述兩類。儘管服務進程不會直接爲用戶所見,但它們通常都在作着用戶所關心的事情(好比在後臺播放mp3或者從網上下載東西)。因此係統會盡可能維持它們的運行,除非系統內存不足以維持前臺進程和可視進程的運行須要。

4.         背景進程包含目前不爲用戶所見的activityActivity對象的 onStop() 方法已被調用)。這些進程與用戶體驗沒有直接的聯繫,能夠在任意時間被殺死以回收內存供前臺進程、可視進程以及服務進程使用。通常來講,會有不少背景進程 運行,因此它們通常存放於一個LRU(最後使用)列表中以確保最後被用戶使用的activity最後被殺死。若是一個activity正確的實現了生命周 期方法,並捕獲了正確的狀態,則殺死它的進程對用戶體驗不會有任何不良影響。

5.         空進程不包含任何活動應用程序組件。這種進程存在的惟一緣由是作爲緩存以改善組件再次於其中運行時的啓動時間。系統常常會殺死這種進程以保持進程緩存和系統內核緩存之間的平衡。

Android會依據進程中當前活躍組件的重要程度來儘量高的估量一個進程的級別。好比說,若是一個進程中同時有一個服務和一個可視的activity,則進程會被斷定爲可視進程,而不是服務進程。

此外,一個進程的級別可能會因爲其它進程依賴於它而升高。一個爲其它進程提供服務的進程級別永遠高於使用它服務的進程。好比說,若是A進程中的內容提供者 爲進程B中的客戶端提供服務,或進程A中的服務爲進程B中的組件所綁定,則A進程最低也會被視爲與進程B擁有一樣的重要性。

     爲運行着一個服務的進程重要級別總高於一個背景activity。因此一個activity以啓動一個服務的方式啓動一個長時間運行過程比簡單的衍生一個 線程來進行處理要好。尤爲是當處理過程比activity自己存在時間要長的狀況之下。咱們以背景音樂播放和上傳一個相機拍攝的圖片至網站上爲例。使用服 務則不論activity發生何事,都至少能夠保證操做擁有服務進程的權限。如上一節廣播接收器生命週期 所提到的,這也正是廣播接收器使用服務,而不是使用線程來處理耗時任務的緣由。

相關文章
相關標籤/搜索