應用程式基礎(Application Fundamentals)
從很多方面來看,每個Android應用程式都存在于它自己的世界之中:
* 預設情況下,每個應用程式均運作于它自己的Linux程序中。當應用程式中的任意代碼開始執行時,Android啟動一個程序,而當不再需要此程序而其它應用程式又需要系統資源時,則關閉這個程序。
* 每個程序都運作于自己的Java虛拟機(VM)中。是以應用程式代碼實際上與其它應用程式的代碼是隔絕的。
* 預設情況下,每個應用程式均被賦予一個唯一的Linux使用者ID,并加以權限設定,使得應用程式的檔案僅對這個使用者、這個應用程式可見。當然,也有其它的方法使得這些檔案同樣能為别的應用程式所通路。
使兩個應用程式共有同一個使用者ID是可行的,這種情況下他們可以看到彼此的檔案。從系統資源維護的角度來看,擁有同一個ID的應用程式也将在運作時使用同一個Linux程序,以及同一個虛拟機。
應用程式元件(Application Components)
Android的核心功能之一就是一個應用程式可以使用其它應用程式的元素(如果那個應用程式允許的話)。比如說,如果你的應用程式需要一個圖檔卷動列 表,而另一個應用程式已經開發了一個合用的而又允許别人使用的話,你可以直接調用那個卷動清單來完成工作,而不用自己再開發一個。你的應用程式并沒有吸納 或連結其它應用程式的代碼,它隻是在有需求的時候啟動了其它應用程式的那個功能部分。
為達到這個目的,系統必須在一個應用程式的一部分被需要時啟動這個應用程式,并将那個部分的Java對象執行個體化。與在其它系統上的應用程式不同,Android應用程式沒有為應用準備一個單獨的程式入口(比如說,沒有<code>main()</code>方法), 而是為系統依照需求執行個體化提供了基本的元件。共有四種元件類型:
Activities
* 一個應用程式可以隻有一個activity,或者,如剛才提到的短信應用程式那樣,包含很多個。每個activity的作用,以及其數目,自然取決于應用 程式及其設計。一般情況下,總有一個應用程式被标記為使用者在應用程式啟動的時候第一個看到的。從一個activity轉向另一個的方式是靠目前的 activity啟動下一個。
* 每個activity都被給予一個預設的視窗以進行繪制。一般情況下,這個視窗是滿屏的,但它也可以是一個小的位于其它視窗之上的浮動視窗。一個 activity也可以使用超過一個的視窗──比如,在activity運作過程中彈出的一個供使用者反應的小對話框,或是當使用者選擇了螢幕上特定項目後顯 示的必要資訊。
服務(Services)
* 一個媒體播放器播放播放清單中的曲目是一個不錯的例子。播放器應用程式可能有一個或多個activity來給使用者選擇歌曲并進行播放。然而,音樂播放這個 任務本身不應該為任何activity所處理,因為使用者期望在他們離開播放器應用程式而開始做别的事情時,音樂仍在繼續播放。為達到這個目的,媒體播放器 activity應該啟用一個運作于背景的服務。而系統将在這個activity不再顯示于螢幕之後,仍維持音樂播放服務的運作。
* 你可以連接配接至(綁定)一個正在運作的服務(如果服務沒有運作,則啟動之)。連接配接之後,你可以通過那個服務暴露出來的接口與服務進行通訊。對于音樂服務來說,這個接口可以允許使用者暫停、回退、停止以及重新開始播放。
廣播接收器(Broadcast receivers)
* 廣播接收器是一個專注于接收廣播通知資訊,并做出對應處理的元件。很多廣播是源自于系統代碼的──比如,通知時區改變、電池電量低、拍攝了一張照片或者使用者改變了語言選項。應用程式也可以進行廣播──比如說,通知其它應用程式一些資料下載下傳完成并處于可用狀态。
内容提供者(Content providers)
每當出現一個需要被特定元件處理的請求時,Android會確定那個元件的應用程式程序處于運作狀态,或在必要的時候啟動它。并確定那個相應元件的執行個體的存在,必要時會建立那個執行個體。
激活元件Activating components: intents
對于每種元件來說,激活的方法是不同的:
關閉元件(Shutting down components)
内容提供者僅在響應ContentResolver提出請求的時候激活。而一個廣播接收器僅在響應廣播資訊的時候激活。是以,沒有必要去顯式的關閉這些元件。
而activity則不同,它提供了使用者界面,并與使用者進行會話。是以隻要會話依然持續,哪怕對話過程暫時停頓,它都會一直保持激活狀态。與此相似,服務也會在很長一段時間内保持運作。是以Android為關閉activity和服務提供了一系列的方法。
manifest檔案(The manifest file)
當Android啟動一個應用程式元件之前,它必須知道那個元件是存在的。是以,應用程式會在一個manifest檔案中聲明它的元件,這個檔案會被打包到Android包中。這個.apk檔案還将涵括應用程式的代碼、檔案以及其它資源。
這個manifest檔案以XML作為結構格式,而且對于所有應用程式,都叫做AndroidManifest.xml。為聲明一個應用程式元件,它還會 做很多額外工作,比如指明應用程式所需連結到的庫的名稱(除了預設的Android庫之外)以及聲明應用程式期望獲得的各種權限。
但manifest檔案的主要功能仍然是向Android聲明應用程式的元件。舉例說明,一個activity可以如下聲明:
Intent過濾器(Intent filters)
Intent對象可以被顯式的指定目标元件。如果進行了這種指定,Android會找到這個元件(依據manifest檔案中的聲明)并激活它。但如果 Intent沒有進行顯式的指定,Android就必須為它找到對于intent來說最合适的元件。這個過程是通過比較Intent對象和所有可能對象的intent過濾器完成的。元件的intent過濾器會告知Android它所能處理的intent類型。如同其它相對于元件很重要的資訊一樣,這些是在manifest檔案中進行聲明的。這裡是上面執行個體的一個擴充,其中加入了針對activity的兩個intent過濾器聲明:
示例中的第一個過濾器──action “android.intent.action.MAIN”和類别“android.intent.category.LAUNCHER”的組合──是通常具有的。它标明了這個activity将在應用程式加載器中顯示,就是使用者在裝置上看到的可供加載的應用程式清單。換句話說,這個activity是應用程式的入口,是使用者選擇運作這個應用程式後所見到的第一個activity。
第二個過濾器聲明了這個activity能被賦予一種特定類型的資料。
元件可以擁有任意數量的intent過濾器,每個都會聲明一系列不同的能力。如果它沒有包含任何過濾器,它将隻能被顯式聲明了目标元件名稱的intent激活。
Activity和任務(Activities and Tasks)
如前所述,一個activity可以啟動另外一個,甚至包括與它不處于同一應用程式之中的。舉個例子說,假設你想讓使用者看到某個地方的街道地圖。而已經存 在一個具有此功能的activity了,那麼你的activity所需要做的工作就是把請求資訊放到一個Intent對象裡面,并把它傳遞給startActivity()。于是地圖浏覽器就會顯示那個地圖。而當使用者按下BACK鍵的時候,你的activity又會再一次的顯示在螢幕上。
對于使用者來說,這看起來就像是地圖浏覽器是你activity所在的應用程式中的一個組成部分,其實它是在另外一個應用程式中定義,并運作在那個應用程式的程序之中的。Android将這兩個activity放在同一個任務中 來維持一個完整的使用者體驗。簡單的說,任務就是使用者所體驗到的“應用程式”。它是安排在一個堆棧中的一組相關的activity。堆棧中的根 activity就是啟動了這整個任務的那個──一般情況下,它就是使用者在應用程式加載器中所選擇的。而堆棧最上方的activity則是目前運作的── 使用者直接對其進行操作的。當一個activity啟動另外一個的時候,新的activity就被壓入堆棧,并成為目前運作的activity。而前一個 activity仍保持在堆棧之中。當使用者按下BACK鍵的時候,目前activity出棧,而前一個恢複為目前運作的activity。
堆棧中儲存的其實是對象,是以如果發生了諸如需要多個地圖浏覽器的情況,就會使得一個任務中出現多個同一Activity子類的執行個體同時存在,堆棧會為每個執行個體單獨開辟一個入口。堆棧中的Activity永遠不會重排,隻會壓入或彈出。
任務其實就是activity的堆棧,而不是manifest檔案中的一個類或者元素。是以你無法撇開activity而為一個任務設定一個值。而事實上 整個任務使用的值是在根activity中設定的。比如說,下一節我們會談及“任務的affinity”,從affinity中讀出的值将會設定到任務的 根activity之中。
任務中的所有activity是作為一個整體進行移動的。整個的任務(即activity堆棧)可以移到前台,或退至背景。舉個例子說,比如目前任務在堆 棧中存有四個activity──三個在目前activity之下。當使用者按下HOME鍵的時候,回到了應用程式加載器,然後選擇了一個新的應用程式(也 就是一個新任務)。則目前任務遁入背景,而新任務的根activity顯示出來。然後,過了一小會兒,使用者再次回到了應用程式加載器而又選擇了前一個應用程式(上一個任務)。于是那個任務,帶着它堆棧中所有的四個activity,再一次的到了前台。當使用者按下BACK鍵的時候,螢幕不會顯示出使用者剛才離開的activity(上一個任務的根activity)。取而代之,目前任務的堆棧中最上面的activity被彈出,而同一任務中的上一個activity顯示了出來。
我們剛才所說的這些關鍵Intent标記如下:
FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
FLAG_ACTIVITY_SINGLE_TOP
而關鍵的<activity>屬性是:
taskAffinity
launchMode
allowTaskReparenting
clearTaskOnLaunch
alwaysRetainTaskState
finishOnTaskLaunch
接下來的一節會描述這些标記以及屬性的作用,它們是如何互相影響的,以及控制它們的使用時必須考慮到的因素。
任務共用性和新任務Affinities and new tasks
預設情況下,一個應用程式中的activity互相之間會有一種Affinity──也就是說,它們首選都歸屬于一個任務。然而,可以在<code><</code>activity<code>></code><code>元素中把每個</code>activity<code>的</code>taskAffinity屬 性設定為一個獨立的affinity。于是在不同的應用程式中定義的activity可以享有同一個affinity,或者在同一個應用程式中定義的 activity有着不同的affinity。affinity在兩種情況下生效:當加載activity的Intent對象包含了FLAG_ACTIVITY_NEW_TASK 标記,或者當activity的allowTaskReparenting屬性設定為“true”。
如前所述,在預設情況下,一個新activity被另外一個調用了startActivity()方法的activity載入了任務之中。并壓入了調用者所在的堆棧。然而,如果傳遞給startActivity()的Intent對象包含了FLAG_ACTIVITY_NEW_TASK标記,系統會為新activity安排另外一個任務。一般情況下,如同标記所暗示的那樣,這會是一個新任務。然而,這并不是必然的。如果已經存在了一個與新activity有着同樣affinity的任務,則activity會載入那個任務之中。如果沒有,則啟用新任務。
如果一個activity将allowTaskReparenting屬 性設定為“true”。它就可以從初始的任務中轉移到與其擁有同一個affinity并轉向前台的任務之中。比如說,一個旅行應用程式中包含的預報所選城 市的天氣情況的activity。它與這個應用程式中其它的activity擁有同樣的affinity(預設的affinity)而且允許重定父級。你 的另一個activity啟動了天氣預報,于是它就會與這個activity共處與同一任務之中。然而,當那個旅行應用程式再次回到前台的時候,這個天氣 預報activity就會被再次安排到原先的任務之中并顯示出來。
如果在使用者的角度看來,一個.apk檔案中包含了多于一個的“應用程式”,你可能會想要為它們所轄的activity安排不一樣的affinity。
加載模式(Launch modes)
"standard" (預設模式)
"singleTop"
"singleTask"
"singleInstance"
這些模式之間的差異主要展現在四個方面:
相反,對“<code>singleTask</code>”和“<code>singleInstance</code>”模式而言,activity總是位于任務的根部。正是它們定義了一個任務,是以它們絕不會被載入到其它任務之中。
* activity是否可以存在多個執行個體。一個“standard”或“singleTop”的activity可以被多次初始化。它們可以歸屬于多個任務,而一個任務也可以擁有同一activity的多個執行個體。
相反,對“singleTask”和“singleInstance”的activity被限定于隻能有一個執行個體。因為這些activity都是任務的起源,這種限制意味着在一個裝置中同一時間隻允許存在一個任務的執行個體。
* 在執行個體所在的任務中是否會有别的activity。一個“singleInstance”模式的activity将會是它所在的任務中唯一的activity。如果它啟動了别的activity,那個activity将會依據它自己的加載模式加載到其它的任務中去──如同在intent中設定了FLAG_ACTIVITY_NEW_TASK 标記一樣的效果。在其它方面,“singleInstance”模式的效果與“singleTask”是一樣的。
剩下的三種模式允許一個任務中出現多個activity。“singleTask”模式的activity将是任務的根activity,但它可以啟動别的activity并将它們置入所在的任務中。“standard”和“singleTop”activity則可以在堆棧的任意位置出現。
* 是否要載入新的類執行個體以處理新的intent。對預設的"standard"模式來說,對于每個新intent都會建立一個新的執行個體以進行響應,每個執行個體僅處理一個intent。“singleTop”模式下,如果activity位于目的任務堆棧的最上面,則重用目前現存的activity來處理新的intent。如果它不是在堆棧頂部,則不會發生重用。而是建立一個新執行個體來處理新的intent并将其推入堆棧。
舉例來說,假設一個任務的堆棧由根activityA和activity B、C和位于堆棧頂部的D組成,即堆棧A-B-C-D。一個針對D類型的activity的intent抵達的時候,如果D是預設的“standard”加載模式,則建立并加載一個新的類執行個體,于是堆棧變為A-B-C-D-D。 然而,如果D的載入模式為“singleTop”,則現有的執行個體會對新intent進行處理(因為它位于堆棧頂部)而堆棧保持A-B-C-D的形态。
換言之,如果新抵達的intent是針對B類型的activity,則無論B的模式是“standard”還是“singleTop” ,都會加載一個新的B的執行個體(因為B不位于堆棧的頂部),而堆棧的順序變為A-B-C-D-B。
如前所述,“singleTask”或“singleInstance”模式的activity永遠不會存在多于一個執行個體。是以執行個體将處理所有新的intent。一個“singleInstance”模式的activity永遠保持在堆棧的頂部(因為它是那個堆棧中唯一的一個activity),是以它一直堅守在處理intent的崗位上。然而,對一個“singleTask”模式的activity來說,它上面可能有,也可能沒有别的activity和它處于同一堆棧。<code>在有的情況下,它就不在能夠處理</code>intent<code>的位置上,</code>則那個intent将被舍棄。(即便在intent被舍棄的情況下,它的抵達仍将使這個任務切換至前台,并一直保留)
請注意,當一個新的activity執行個體被建立以處理新的intent的時候,使用者總可以按下BACK鍵來回到前面的狀态(回到前一個 activity)。但當使用現存的activity來處理新intent的時候,使用者是不能靠按下BACK鍵回到當這個新intent抵達之前的狀态 的。
清理堆棧(Clearing the stack)
如果使用者離開一個任務很長一段時間,系統會清理該任務中除了根activity之外的所有activity。當使用者再次回到這個任務的時候,除了隻剩下初 始化activity尚存之外,其餘都跟使用者上次離開它的時候一樣。這樣做的原因是:在一段時間之後,使用者再次回到一個任務的時候,他們更期望放棄他們之 前的所作所為,做些新的事情。
這些屬于預設行為,另外,也存在一些activity的屬性用以控制并改變這些行為:
如果一個任務的根activity中此屬性設定為“true”,則上述預設行為不會發生。任務将在很長的一段時間内保留它堆棧内的所有activity。
如果一個任務的根activity中此屬性設定為“true”,則每當使用者離開這個任務和傳回它的時候,堆棧都會被清空至隻留下rootactivity。換句話說,這是alwaysRetainTaskState的另一個極端。哪怕僅是過了一小會兒,使用者回到任務時,也是見到它的初始狀态。
這個屬性與clearTaskOnLaunch屬性相似,但它僅作用于單個的activity,而不是整個的task。而且它可以使任意activity都被清理,甚至根activity也不例外。當它設定為“true”的時候,此activity僅做為任務的一部分存在于目前回話中,一旦使用者離開并再次回到這個任務,此activity将不複存在。
FLAG_ACTIVITY_CLEAR_TOP與FLAG_ACTIVITY_NEW_TASK經常合并使用。這時,這些标記提供了一種定位其它任務中現存的activity并将它們置于可以對intent做出響應的位置的方法。
啟動任務(Starting tasks)
第二個能力相當重要:使用者必須可以離開一個任務,并在一段時間後傳回它。出于這個考慮,加載模式被設定為“singleTask”和“singleInstance”的activity總是會初始化一個新任務,這樣的activity僅能用于指定了一個MAIN和LAUNCHER過濾器的情況之下。我們來舉例說明如果沒指定過濾器的情況下會發生的事情:一個intent加載了一個“singleTask”的activity,初始化了一個新任務,使用者在這個任務中花費了一些時間來完成工作。然後使用者按下了HOME鍵。于是任務被要求轉至背景并被主螢幕所掩蓋。因為它并沒有在應用程式加載器中顯示圖示,這将導緻使用者無法再傳回它。
類似的困境也可由FLAG_ACTIVITY_NEW_TASK标記引起。如果此标記使一個activity啟動了一個新任務繼而使用者按下了HOME鍵離開了它,則使用者必須要有一些方法再次回到這個任務。一些實體(諸如通知管理器)總是在另外的任務中啟動新activity,而不是做為它們自己的一部分,是以它們總是将FLAG_ACTIVITY_NEW_TASK标記包含在intent裡面并傳遞給startActivity()。如果你寫了一個能被外部實體使用這個标記調用的activity,你必須注意要給使用者留一個傳回這個被外部實體啟動的任務的方法。
程序和線程(Processes and Threads)
當一個應用程式開始運作它的第一個元件時,Android會為它啟動一個Linux程序,并在其中執行一個單一的線程。預設情況下,應用程式所有的元件均在這個程序的這個線程中運作。
然而,你也可以安排元件在其他程序中運作,而且可以為任意程序衍生出其它線程。
程序(Processes)
元件運作所在的程序由manifest檔案所控制。元件元素<code>——<</code>activity<code>></code>, <code><</code>service<code>></code>, <code><</code>receiver<code>></code>和<code><</code>provider<code>></code>——都有一個 process 屬性來指定元件應當運作于哪個程序之内。這些屬性可以設定為使每個元件運作于它自己的程序之内,或一些元件共享一個程序而其餘的元件不這麼做。它們也可以 設定為令不同應用程式的元件在一個程序中運作——使應用程式的組成部分共享同一個Linux使用者ID并賦以同樣的權限。<code><</code>application<code>></code>元素也有一個process屬性,以設定所有元件的預設值。
在可用記憶體不足而又有一個正在為使用者進行服務的程序需要更多記憶體的時候,Android有時候可能會關閉一個程序。而在這個程序中運作着的應用程式也是以被銷毀。當再次出現需要它們進行處理的工作的時候,會為這些元件重新建立程序。
線程(Threads)
盡管你可以把你的應用程式限制于一個單獨的程序中,有時,你仍然需要衍生出一個線程以處理背景任務。因為使用者界面必須非常及時的對使用者操作做出響應,所 以,控管activity的線程不應用于處理一些諸如網絡下載下傳之類的耗時操作。所有不能在瞬間完成的任務都應安排到不同的線程中去。
遠端方法調用(Remote procedure calls)
Android有一個輕量級的遠端方法調用(RPC)機制:即在本地調用一個方法,但在遠端(其它的程序中)進行處理,然後将結果傳回調用者。這将方法調用及其附屬的資料以系統可以了解的方式進行分離,并将其從本地程序和本地位址空間傳送至遠端過程和遠端位址空間,并在那裡重新裝配并對調用做出反應。傳回 的結果将以相反的方向進行傳遞。Android提供了完成這些工作所需的所有的代碼,以使你可以集中精力來實作RPC接口本身。
RPC接口可以隻包括方法。即便沒有傳回值,所有方法仍以同步的方式執行(本地方法阻塞直至遠端方法結束)。
一般情況下,遠端過程是被一個服務所管理的(因為服務可以通知系統關于程序以及它連接配接到别的程序的資訊)。它包含着 aidl工具産生的接口檔案和實作了RPC方法的Stub的子類。而用戶端隻需要包括aidl工具産生的接口檔案。
下面将說明服務與其用戶端之間的連接配接是如何建立的:
* 如果服務接受了連接配接,Android将會調用用戶端的onServiceConnected() 方法,并傳遞給它一個IBinder對象,它是由服務所管理的Stub的子類的代理。通過這個代理,用戶端可以對遠端服務進行調用。
線程安全方法(Thread-safe methods)
在一些情況下,你所實作的方法有可能會被多于一個的線程所調用,是以它們必須被寫成線程安全的。
對于我們上一節所讨論的RPC機制中的可以被遠端調用的方法來說,這是必須首先考慮的。如果針對一個IBinder對象中實作的方法的調用源自這個 IBinder對象所在的程序時,這個方法将會在調用者的線程中執行。然而,如果這個調用源自其它的程序,則這個方法将會在一個線程池中選出的線程中運 行,這個線程池由Android加以管理,并與IBinder存在于同一程序内;這個方法不會在程序的主線程内執行。反過來說,一個服務的 onBind() 方法應為服務程序的主線程所調用,而實作了由 onBind() 傳回的對象(比如說,一個實作了RPC方法的Stub的子類)的方法将為池中的線程所調用。因為服務可以擁有多于一個的用戶端,而同一時間,也會有多個池中的線程調用同一個IBinder方法。是以IBinder方法必須實作為線程安全的。
應用程式元件有其生命周期──由Android初始化它們以相應intent直到這個執行個體被摧毀。在此之間,它們有時是激活的有時則相反。或者,如果它是 一個activity,則是可為使用者所見或者不能。這一節讨論了activity、服務以及廣播接收器的生命周期,包括它們在生命周期中的狀态、在狀态之 間轉變時通知你的方法、以及當這些程序被關閉或執行個體被摧毀時,這些狀态産生的效果。
Activity生命周期(Activity lifecycle)
一個activity主要有三個狀态:
* 當在螢幕前台時(位于目前任務堆棧的頂部),它是活躍或運作的狀态。它就是相應使用者操作的activity。
* 當它失去焦點但仍然對使用者可見時,它處于暫停狀态。即是:在它之上有另外一個activity。這個activity也許是透明的,或者未能完全遮蔽全屏,是以被暫停的activity仍對使用者可見。暫停的activity仍然是存活狀态(它保留着所有的狀态和成員資訊并連接配接至視窗管理器),但當系統處于極低記憶體的情況下,仍然可以殺死這個activity。
* 如果它完全被另一個activity覆寫是,它處于停止狀态。它仍然保留所有的狀态和成員資訊。然而它不在為使用者可見,是以它的視窗将被隐藏,如果其它地方需要記憶體,則系統經常會殺死這個activity。
當一個activity從這個狀态轉變到另一個狀态時,它被以下列protected方法所通知:
void onCreate(Bundle savedInstanceState)
void onStart()
void onRestart()
void onResume()
void onPause()
void onStop()
void onDestroy()
調用父類(Calling into the superclass)
所有activity生命周期方法的實作都必須先調用其父類的版本。比如說:
總得來說,這七個方法定義了一個activity完整的生命周期。實作這些方法可以幫助你監察三個嵌套的生命周期循環:
* 一個activity 完整的生命周期 自第一次調用 onCreate()開始,直至調用onDestroy()為止。activity在onCreate()中設定所有“全局”狀态以完成初始化,而在onDestroy()中釋放所有系統資源。比如說,如果activity有一個線程在背景運作以從網絡上下載下傳資料,它會以 onCreate()<code>建立那個線程,而以</code> 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?)
下一個
接下來始終遵循調用onStart()。
否
onStart()
在activity停止後,在再次啟動之前被調用。
當activity正要變得為使用者所見時被調用。
當activity轉向前台時接下來調用onResume(),在activity變為隐藏時接下來調用onStop()。
onResume()
或
onStop()
在activity開始與使用者進行互動之前被調用。此時activity位于堆棧頂部,并接受使用者輸入。
接下來始終遵循調用onPause()。
<code>onPause()</code>
當系統将要啟動另一個activity時調用。此方法主要用來将未儲存的變化進行持久化,停止類似動畫這樣耗費CPU的動作等。這一切動作應該在短時間内完成,因為下一個activity必須等到此方法傳回後才會繼續。
當activity重新回到前台時接下來調用onResume()。當activity變為使用者不可見時接下來調用onStop()。
是
當activity不再為使用者可見時調用此方法。這可能發生在它被銷毀或者另一個activity(可能是現存的或者是新的)回到運作狀态并覆寫了它。
如果activity再次回到前台跟使用者互動則接下來調用onRestart(),如果關閉activity則接下來調用onDestroy()。
onRestart()
or
onDestroy()
無
請注意上表中可被殺死一列。它标示了在方法傳回後,還沒執行activity的其餘代碼的任意時間裡,系統是否可以殺死包含此activity的程序。三個方法(onPause()、onStop()<code>和</code>onDestroy())被标記為“是”。onPause()是三個中的第一個,它也是唯一一個在程序被殺死之前必然會調用的方法──onStop() 和 onDestroy() 有可能不被執行。是以你應該用 onPause() 來将所有持久性資料(比如使用者的編輯結果)寫入存儲之中。
在可被殺死一列中标記為“否”的方法在它們被調用時将保護activity所在的程序不會被殺死。是以隻有在onPause()<code>方法傳回後到</code>onResume() 方法被調用時,一個activity才處于可被殺死的狀态。在onPause()<code>再次被調用并傳回之前,它不會被系統殺死。</code>
儲存activity狀态(Saving activity state)
當系統而不是使用者自己出于回收記憶體的考慮,關閉了一個activity之後。使用者會期望當他再次回到那個activity的時候,它仍保持着上次離開時的樣子。
與onPause()或先前讨論的其它方法不同,onSaveInstanceState() 和 onRestoreInstanceState() 并不是生命周期方法。它們并不是總會被調用。比如說,Android會在activity易于被系統銷毀之前調用 onSaveInstanceState(),但使用者動作(比如按下了BACK鍵)造成的銷毀則不調用。在這種情況下,使用者沒打算再次回到這個activity,是以沒有儲存狀态的必要。
因為onSaveInstanceState()不是總被調用,是以你應該隻用它來為activity儲存一些臨時的狀态,而不能用來儲存持久性資料。而是應該用onPause()來達到這個目的。
服務生命周期(Coordinating activities)
服務以兩種方式使用:
這兩種模式并不是完全分離的。你可以綁定至一個用 startService()啟動的服務。比如說,一個背景音樂播放服務可以調用startService()并傳遞給它一個包含欲播放的音樂清單的Intent對象來啟動。不久,當使用者想要對播放器進行控制或者檢視目前播放曲目的詳情時,會啟用一個activity,調用bindService()連接配接到服務來完成操作。在這種情況下,直到綁定連接配接關閉stopService() 才會真正停止一個服務。
與activity一樣,服務也有一系列你可以實作以用于監控其狀态變化的生命周期方法。但相對于activity要少一些,隻有三個,而且,它們是public屬性,并非protected:
void onCreate()
void onStart(Intent intent)
倚仗實作這些方法,你監控服務的兩個嵌套的生命周期循環:
服務停止時沒有相應的回調方法──不存在onStop()方法。
如果一個服務允許别的程序綁定,則它還會有以下額外的回調方法以供實作:
IBinder onBind(Intent intent)
boolean onUnbind(Intent intent)
void onRebind(Intent intent)
下圖描繪了服務的回調方法。盡管圖中對由startService 和startService方法啟動的服務做了區分,但要記住,不論一個服務是怎麼啟動的,它都可能允許用戶端的連接配接,是以任何服務都可以接受onBind()和onUnbind()調用。
廣播接收器隻有一個回調方法:
void onReceive(Context curContext, Intent broadcastMsg)
擁有一個活躍狀态的廣播接收器的程序被保護起來而不會被殺死。但僅擁有失活狀态元件的程序則會在其它程序需要它所占有的記憶體的時候随時被殺掉。
這種方式引出了一個問題:如果響應一個廣播資訊需要很長的一段時間,我們一般會将其納入一個衍生的線程中去完成,而不是在主線程内完成它,進而保證使用者互動過程的流暢。如果onReceive()衍生了一個線程并且傳回,則包涵新線程在内的整個程序都被會判為失活狀态(除非程序内的其它應用程式元件仍處于活躍狀态),于是它就有可能被殺掉。這個問題的解決方法是令onReceive()啟動一個新服務,并用其完成任務,于是系統就會知道程序中仍然在處理着工作。
下一節中,我們會讨論更多程序易誤殺的問題。
程序與生命周期(Processes and lifecycles)
Android系統會盡可能長的延續一個應用程式程序,但在記憶體過低的時候,仍然會不可避免需要移除舊的程序。為決定保留或移除一個程序,Android 将每個程序都放入一個“重要性層次”中,依據則是它其中運作着的元件及其狀态。重要性最低的程序首先被消滅,然後是較低的,依此類推。重要性共分五層,依 據重要性清單如下:
1. 前台程序是使用者操作所必須的。當滿足如下任一條件時,程序被認為是處于前台的:
* 一個正在與使用者互動的activity使用着它提供的一個服務。
任一時間下,僅有少數程序會處于前台,僅當記憶體實在無法供給它們維持同時運作時才會被殺死。一般來說,在這種情況下,裝置已然處于使用虛拟記憶體的狀态,必須要殺死一些前台程序以使用者界面保持響應。
2. 可視程序沒有前台元件,但仍可被使用者在螢幕上所見。當滿足如下任一條件時,程序被認為是可視的:
* 它包含了一個綁定至一個可視的activity的服務。
可視程序依然被視為是很重要的,非到不殺死它們便無法維持前台程序運作時,才會被殺死。
5. 空程序不包含任何活動應用程式元件。這種程序存在的唯一原因是做為緩存以改善元件再次于其中運作時的啟動時間。系統經常會殺死這種程序以保持程序緩存和系統核心緩存之間的平衡。
Android會依據程序中目前活躍元件的重要程度來盡可能高的估量一個程序的級别。比如說,如果一個程序中同時有一個服務和一個可視的activity,則程序會被判定為可視程序,而不是服務程序。
此外,一個程序的級别可能會由于其它程序依賴于它而升高。一個為其它程序提供服務的程序級别永遠高于使用它服務的程序。比如說,如果A程序中的内容提供者 為程序B中的用戶端提供服務,或程序A中的服務為程序B中的元件所綁定,則A程序最低也會被視為與程序B擁有同樣的重要性。