Android開發指南-框架主題-基礎知識

應用程序基礎

關鍵類

  1. Activity
  2. Service
  3. BroadcastReceiver
  4. ContentProvider
  5. Intent

Android應用程序使用Java做爲開發 語言。aapt 工 具把編譯後的Java代碼連同其它應用程序需要的數據和資源文件一起打包到一個Android包文件中,這個文件使用.apk做爲擴展名,它是分發應用程 序並安裝到移動設備的媒介,用戶只需下載並安裝此文件到他們的設備。單一.apk文件中的所有代碼被認爲是一個應用程序。

從很多方面來看,每個Android應用程序都存在於它自己的世界之中:

  • 默認情況下,每個應用程序均運行於它自己的Linux進程中。當應用程序中的任意代碼開始執行時,Android啓動一個進程,而當不再需要此進程而其它應用程序又需要系統資源時,則關閉這個進程。
  • 每個進程都運行於自己的Java虛擬機(VM)中。所以應用程序代碼實際上與其它應用程序的代碼是隔絕的。
  • 默認情況下,每個應用程序均被賦予一個唯一的Linux用戶ID,並加以權限設置,使得應用程序的文件僅對這個用戶、這個應用程序可見。當然,也有其它的方法使得這些文件同樣能爲別的應用程序所訪問。

使兩個應用程序共有同一個用戶ID是可行的,這種情況下他們可以看到彼此的文件。從系統資源維護的角度來看,擁有同一個ID的應用程序也將在運行時使用同一個Linux進程,以及同一個虛擬機。

應用程序組件

Android的核心功能之一就是一個應用程序可以使用其它應用程序的元素(如果那個應用程序允許的話)。比如說,如果你的應用程序需要一個圖片捲動列表,而另一個應用程序已經開發 了一個合用的而又允許別人使用的話,你可以直接調用那個捲動列表來完成工作,而不用自己再開發一個。你的應用程序並沒有吸納或鏈接其它應用程序的代碼,它只是在有需求的時候啓動了其它應用程序的那個功能部分。

爲達到這個目的,系統必須在一個應用程序的一部分被需要時啓動這個應用程序,並將那個部分的Java對象實例化。與在其它系統上的應用程序不同,Android應用程序沒有爲應用準備一個單獨的程序入口(比如說,沒有main() 方法), 而是爲系統依照需求實例化提供了基本的組件。共有四種組件類型:

Activity

Activity是爲用戶操作而展示的可視化用戶界面。 比如說,一個activity可以展 示一個菜單項列表供用戶選擇,或者顯示一些包含說明的照片。一個短消息應用程序可以包括一個用於顯示做爲發送對象的聯繫人的列表的activity,一個 給選定的聯繫人寫短信的activity以及翻閱以前的短信和改變設置的activity。儘管它們一起組成了一個內聚的用戶界面,但其中每個 activity都與其它的保持獨立。每個都是以Activity 類爲基類的子類實現。

一個應用程序可以只有一個activity,或者,如剛纔提到的短信應用程序那樣,包含很多個。每個activity的作用,以及其數目,自然取決 於應用程序及其設計。一般情況下,總有一個應用程序被標記爲用戶在應用程序啓動的時候第一個看到的。從一個activity轉向另一個的方式是靠當前的 activity啓動下一個。

每個activity都被給予一個默認的窗口以進行繪製。一般情況下,這個窗口是滿屏的,但它也可以是一個小的位於其它窗口之上的浮動窗口。一個 activity也可以使用超過一個的窗口──比如,在activity運行過程中彈出的一個供用戶反應的小對話框,或是當用戶選擇了屏幕上特定項目後顯 示的必要信息。

窗口顯示的可視內容是由一系列視圖構成的,這些視圖均繼承自 View 基類。每個視圖均控制着窗口中一塊特定的矩形 空 間。父級視圖包含並組織它子視圖的佈局。葉節點視圖(位於視圖層次最底端)在它們控制的矩形中進行繪製,並對用戶對其直接操作做出響應。所以,視圖是 activity與用戶進行交互的界面。比如說,視圖可以顯示一個小圖片,並在用戶指點它的時候產生動作。Android有很多既定的視圖供用戶直接使 用,包括按鈕、文本域、卷軸、菜單項、複選框等等。

視圖層次是由Activity.setContentView() 方法放入activity的窗口之中的。上下文視圖 是位於視圖層次根位置的視圖對象。(參見用戶界面 章節獲取關於視圖及層次的更多信息。)

服務

服務沒有可視化的用戶界面,而是在一段時間內在後臺運行。比如說,一個服務可以在用戶做其它事情的時候在後臺播放背景音樂、從網絡上獲取一些數據或者計算一些東西並提供給需要這個運算結果的activity使用。每個服務都繼承自Service 基類。

一個媒體播放器播放播放列表中的曲目是一個不錯的例子。播放器應用程序可能有一個或多個activity來給用戶選擇歌曲並進行播放。然而,音樂播 放這個任務本身不應該爲任何activity所處理,因爲用戶期望在他們離開播放器應用程序而開始做別的事情時,音樂仍在繼續播放。爲達到這個目的,媒體 播放器activity應該啓用一個運行於後臺的服務。而系統將在這個activity不再顯示於屏幕之後,仍維持音樂播放服務的運行。

你可以連接至(綁定)一個正在運行的服務(如果服務沒有運行,則啓動之)。連接之後,你可以通過那個服務暴露出來的接口與服務進行通訊。對於音樂服務來說,這個接口可以允許用戶暫停、回退、停止以及重新開始播放。

如同activity和其它組件一樣,服務運行於應用程序進程的主線程內。所以它不會對其它組件或用戶界面有任何干擾,它們一般會派生一個新線程來進行一些耗時任務(比如音樂回放)。參見下述 進程和線程

廣播接收器

廣播接收器是一個專注於接收廣播通知信息,並做出對應處理的組件。 很多廣播是源自於系統代碼的──比如,通知時區改變、電池電量低、拍攝了一張照片或者用戶改變了語言選項。應用程序也可以進行廣播──比如說,通知其它應用程序一些數據下載完成並處於可用狀態。


應用程序可以擁有任意數量的廣播接收器以對所有它感興趣的通知信息予以響應。所有的接收器均繼承自BroadcastReceiver 基類。
廣播接收器沒有用戶界面。然而,它們可以啓動一個activity來響應它們收到的信息,或者用NotificationManager 來通知用戶。通知可以用很多種方式來吸引用戶的注意力──閃動背燈、震動、播放聲音等等。一般來說是在狀態欄上放一個持久的圖標,用戶可以打開它並獲取消息。

內容提供者

內容提供者 將一些特定的應用程序數據供給其它應用程序使用。數據可以存儲於文件系統、SQLite數據庫或其它方式。內容提供者繼承於ContentProvider 基類,爲其它應用程序取用和存儲它管理的數據實現了一套標準方法。然而,應用程序並不直接調用這些方法,而是使用一個 ContentResolver 對象,調用它的方法作爲替代。ContentResolver可以與任意內容提供者進行會話,與其合作來對所有相關交互通訊進行管理。

參閱獨立的內容提供者 章節獲得更多關於使用內容提供者的內容。

每當出現一個需要被特定組件處理的請求時,Android會確保那個組件的應用程序進程處於運行狀態,或在必要的時候啓動它。並確保那個相應組件的實例的存在,必要時會創建那個實例。

激活組件:intent

當接收到ContentResolver發出的請求後,內容提供者被激活。而其它三種組件──activity、服務和廣播接收器被一種叫做intent 的異步消息所激活。intent是一個保存着消息內容的Intent 對 象。對於activity和服務來說,它指明瞭請求的操作名稱以及作爲操作對象的數據的URI和其它一些信息。比如說,它可以承載對一個activity 的請求,讓它爲用戶顯示一張圖片,或者讓用戶編輯一些文本。而對於廣播接收器而言,Intent對象指明瞭聲明的行爲。比如,它可以對所有感興趣的對象聲 明照相按鈕被按下。

對於每種組件來說,激活的方法是不同的:

  • 通過傳遞一個Intent對象至 Context.startActivity() Activity.startActivityForResult() 以載入(或指定新工作給)一個activity。相應的activity可以通過調用 getIntent() 方法來查看激活它的intent。Android通過調用activity的onNewIntent() 方法來傳遞給它繼發的intent。
    一個activity經常啓動了下一個。如果它期望它所啓動的那個activity返回一個結果,它會以調用startActivityForResult()來取代 startActivity()。比如說,如果它啓動了另外一個activity以使用戶挑選一張照片,它也許想知道哪張照片被選中了。結果將會被封裝在一個Intent對象中,並傳遞給發出調用的activity的 onActivityResult() 方法。
  • 通過傳遞一個Intent對象至Context.startService() 將啓動一個服務(或給予正在運行的服務以一個新的指令)。Android調用服務的 onStart() 方法並將Intent對象傳遞給它。
    與此類似,一個Intent可以被調用組件傳遞給 Context.bindService() 以獲取一個正在運行的目標服務的連接。這個服務會經由onBind() 方法的調用獲取這個Intent對象(如果服務尚未啓動,bindService()會先啓動它 )。比如說,一個activity可以連接至前述的音樂回放服務,並提供給用戶一個可操作的(用戶界面)以對回放進行控制。這個activity可以調用 bindService() 來建立連接,然後調用服務中定義的對象來影響回放。
    後面一節:
    遠程方法調用 將更詳細的闡明如何綁定至服務。
  • 應用程序可以憑藉將Intent對象傳遞給 Context.sendBroadcast() Context.sendOrderedBroadcast() , 以及Context.sendStickyBroadcast() 和其它類似方法來產生一個廣播。Android會調用所有對此廣播有興趣的廣播接收器的 onReceive() 方法,將intent傳遞給它們。

欲瞭解更多intent消息的信息,請參閱獨立章節 Intent和Intent濾過器

關閉組件

內容提供者僅在響應ContentResolver提出請求的時候激活。而一個廣播接收器僅在響應廣播信息的時候激活。所以,沒有必要去顯式的關閉這些組件。

而activity則不同,它提供了用戶界面,並與用戶進行會話。所以只要會話依然持續,哪怕對話過程暫時停頓,它都會一直保持激活狀態。與此相似,服務也會在很長一段時間內保持運行。所以Android爲關閉activity和服務提供了一系列的方法。

  • 可以通過調用它的finish() 方法來關閉一個activity。一個activity可以通過調用另外一個activity(它用startActivityForResult() 啓動 的)的finishActivity() 方法來關閉它。
  • 服務可以通過調用它的stopSelf() 方法來停止,或者調用 Context.stopService()

系統也會在組件不再被使用的時候或者Android需要爲活動組件聲明更多內存的時候關閉它。後面的 組件的生命週期 一節,將對這種可能及附屬情況進行更詳細的討論。

manifest文件

當Android啓動一個應用程序組件之前,它必須知道那個組件是存在的。所以,應用程序會在一個manifest文件中聲明它的組件,這個文件會被打包到Android包中。這個.apk文件還將涵括應用程序的代碼、文件以及其它資源。

這個manifest文件以XML作爲結構格式,而且對於所有應用程序,都叫做AndroidManifest.xml。爲聲明一個應用程序組件, 它還會做很多額外工作,比如指明應用程序所需鏈接到的庫的名稱(除了默認的Android庫之外)以及聲明應用程序期望獲得的各種權限。

但manifest文件的主要功能仍然是向Android聲明應用程序的組件。舉例說明,一個activity可以如下聲明:

<?xml version="1.0" encoding="utf-8"?>
<manifest . . . >
<application . . . >
<activity android :name="com.example.project.FreneticActivity"
android :icon="@drawable/small_pic.png"
android :label="@string/freneticLabel"
. . . >
</activity>
. . .
</application>
</manifest>

<activity> 元素的name屬性指定了實現了這個activity的 Activity 的子類。icon和label屬性指向了包含展示給用戶的此activity的圖標和標籤的資源文件。

其它組件也以類似的方法聲明──<service> 元素用於聲明服務, <receiver> 元素用於聲明廣播接收器,而 <provider> 元素用於聲明內容提供者。 manifest文件中未進行聲明的activity、服務以及內容提供者將不爲系統所見,從而也就不會被運行。然而,廣播接收器既可以在manifest文件中聲明,也可以在代碼中進行動態的創建,並以調用Context.registerReceiver() 的方式註冊至系統。

欲更多瞭解如何爲你的應用程序構建manifest文件,請參閱AndroidManifest.xml文件 一章。

Intent過濾器

Intent對象可以被顯式的指定目標組件。如果進行了這種指定,Android會找到這個組件(依據manifest文件中的聲明)並激活它。但 如果Intent沒有進行顯式的指定,Android就必須爲它找到對於intent來說最合適的組件。這個過程是通過比較Intent對象和所有可能對 象的intent過濾器 完成的。組件的intent過濾器會告知Android它所能處理的intent類型。如同其它相對於組件很重要的信息一樣,這些是在manifest文件中進行聲明的。這裏是上面實例的一個擴展,其中加入了針對activity的兩個intent過濾器聲明:

<?xml version="1.0" encoding="utf-8"?>
<manifest . . . >
<application . . . >
<activity android :name="com.example.project.FreneticActivity"
android :icon="@drawable/small_pic.png"
android :label="@string/freneticLabel"
. . . >
<intent-filter . . . >
<action android :name="android.intent.action.MAIN" />
<category android :name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter . . . >
<action android :name="com.example.project.BOUNCE" />
<data android :type="image/jpeg" />
<category android :name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
. . .
</application>
</manifest>

示例中的第一個過濾器──action “android .intent.action.MAIN ”和類別“android .intent.category.LAUNCHER ”的組合──是通常具有的。它標明瞭這個activity將在應用程序加載器中顯示,就是用戶在設備上看到的可供加載的應用程序列表。換句話說,這個activity是應用程序的入口,是用戶選擇運行這個應用程序後所見到的第一個activity。

第二個過濾器聲明瞭這個activity能被賦予 一種特定類型的數據。

組件可以擁有任意數量的intent過濾器,每個都會聲明一系列不同的能力。如果它沒有包含任何過濾器,它將只能被顯式聲明瞭目標組件名稱的intent激活。

對於在代碼中創建並註冊的廣播接收器來說,intent過濾器將被直接以 IntentFilter 對象實例化。其它過濾器則在manifest文件中設置。

欲獲得更多intent過濾器的信息,請參閱獨立章節: Intent和Intent過濾器

Activity和任務

如前所述,一個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顯示了出來。

上述的種種即是activity和任務的默認行爲模式。但是有一些方法可以改變所有這一切。activity和任務的聯繫、任務中activity 的行爲方式都被啓動那個activity的Intent對象中設置的一系列標記和manifest文件中那個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

接下來的一節會描述這些標記以及屬性的作用,它們是如何互相影響的,以及控制它們的使用時必須考慮到的因素。

Affinity(吸引力)和新任務

默認情況下,一個應用程序中的activity相互之間會有一種Affinity──也就是說,它們首選都歸屬於一個任務。然而,可以在<activity>元素中把每個activity的 taskAffinity 屬 性設置爲一個獨立的affinity。於是在不同的應用程序中定義的activity可以享有同一個affinity,或者在同一個應用程序中定義的 activity有着不同的affinity。affinity在兩種情況下生效:當加載activity的Intent對象包含了FLAG_ACTIVITY_NEW_TASK 標記,或者當activity的allowTaskReparenting 屬性設置爲“true”。

FLAG_ACTIVITY_NEW_TASK 標記

如前所述,在默認情況下,一個新activity被另外一個調用了startActivity() 方法的activity載入了任務之中。並壓入了調用者所在的堆棧。然而,如果傳遞給startActivity() 的Intent對象包含了FLAG_ACTIVITY_NEW_TASK 標記,系統會爲新activity安排另外一個任務。一般情況下,如同標記所暗示的那樣,這會是一個新任務。然而,這並不是必然的。如果已經存在了一個與新activity有着同樣affinity的任務,則activity會載入那個任務之中。如果沒有,則啓用新任務。

allowTaskReparenting 屬性

如果一個activity將allowTaskReparenting 屬 性設置爲“true”。它就可以從初始的任務中轉移到與其擁有同一個affinity並轉向前臺的任務之中。比如說,一個旅行應用程序中包含的預報所選城 市的天氣情況的activity。它與這個應用程序中其它的activity擁有同樣的affinity(默認的affinity)而且允許重定父級。你 的另一個activity啓動了天氣預報,於是它就會與這個activity共處與同一任務之中。然而,當那個旅行應用程序再次回到前臺的時候,這個天氣 預報activity就會被再次安排到原先的任務之中並顯示出來。

如果在用戶的角度看來,一個.apk文件中包含了多於一個的“應用程序”,你可能會想要爲它們所轄的activity安排不一樣的affinity。

加載模式

<activity> 元素的launchMode 屬性可以設置四種不同的加載模式:

"standard " (默認值)
"singleTop "
"singleTask "
"singleInstance "

這些模式之間的差異主要體現在四個方面:

  • 哪個任務會把持 對intent做出響應的 activity。 對“standard ”和“singleTop ”模式而言,是產生intent(並調用 startActivity() )的任務──除非Intent對象包含FLAG_ACTIVITY_NEW_TASK 標記。而在這種情況下,如同上面 Affinitie和新任務 一節所述,會是另外一個任務。

    相反,對“singleTask ”和“singleInstance ”模式而言,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和它處於同一堆棧。在有的情況下,它就不在能夠處理intent的位置上,則那個intent將被捨棄 。(即便在intent被捨棄的情況下,它的抵達仍將使這個任務切換至前臺,並一直保留)

當一個現存的activity被要求處理一個新的intent的時候,會調用onNewIntent() 方法來將intent對象傳遞至activity。(啓動activity的原始intent對象可以通過調用getIntent() 方法獲得。)

請注意,當一個新的activity實例被創建以處理新的intent的時候,用戶總可以按下BACK鍵來回到前面的狀態(回到前一個 activity)。但當使用現存的activity來處理新intent的時候,用戶是不能靠按下BACK鍵回到當這個新intent抵達之前的狀態 的。

想獲得更多關於加載模式的內容,請參閱 <activity> 元素的描述。

清理堆棧

如果用戶離開一個任務很長一段時間,系統會清理該任務中除了根activity之外的所有activity。當用戶再次回到這個任務的時候,除了只 剩下初始化activity尚存之外,其餘都跟用戶上次離開它的時候一樣。這樣做的原因是:在一段時間之後,用戶再次回到一個任務的時候,他們更期望放棄 他們之前的所作所爲,做些新的事情。

這些屬於默認行爲,另外,也存在一些activity的屬性用以控制並改變這些行爲:

alwaysRetainTaskState 屬性

如果一個任務的根activity中此屬性設置爲“true ”,則上述默認行爲不會發生。任務將在很長的一段時間內保留它堆棧內的所有activity。

clearTaskOnLaunch 屬性

如果一個任務的根activity中此屬性設置爲“true ”,則每當用戶離開這個任務和返回它的時候,堆棧都會被清空至只留下rootactivity。換句話說,這是alwaysRetainTaskState 的另一個極端。哪怕僅是過了一小會兒,用戶回到任務時,也是見到它的初始狀態。

finishOnTaskLaunch 屬性

這個屬性與clearTaskOnLaunch 屬性相似,但它僅作用於單個的activity,而不是整個的task。而且它可以使任意activity都被清理,甚至根activity也不例外。當它設置爲“true ”的時候,此activity僅做爲任務的一部分存在於當前回話中,一旦用戶離開並再次回到這個任務,此activity將不復存在。

此外,還有別的方式從堆棧中移除一個activity。如果一個intent對象包含FLAG_ACTIVITY_CLEAR_TOP 標記,而且目標任務的堆棧中已經存在了一個能夠響應此intent的activity類型的實例。則這個實例之上的所有activity都將被清理以使它位於堆棧的頂部來對intent做出響應。如果此時指定的activity的加載模式爲“standard ”,則它本身也會從堆棧中移除,並加載一個新的實例來處理到來的intent。這是因爲加載模式爲“standard ”的activity總會創建一個新實例來處理新的intent。

FLAG_ACTIVITY_CLEAR_TOPFLAG_ACTIVITY_NEW_TASK 經常合併使用。這時,這些標記提供了一種定位其它任務中現存的activity並將它們置於可以對intent做出響應的位置的方法。

啓動任務

當一個activity被指定一個“android .intent.action.MAIN ”做爲動作,以及“android .intent.category.LAUNCHER ”做爲類別的intent過濾器之後(在前述intent過濾器 一節中已經有了這個示例),它就被設置爲一個任務的入口點。這樣的過濾器設置會在應用程序加載器中爲此activity顯示一個圖標和標籤,以供用戶加載任務或加載之後在任意時間回到這個任務。

第二個能力相當重要:用戶必須可以離開一個任務,並在一段時間後返回它。出於這個考慮,加載模式被設定爲“singleTask ”和“singleInstance ”的activity總是會初始化一個新任務,這樣的activity僅能用於指定了一個MAINLAUNCHER 過濾器的情況之下。我們來舉例說明如果沒指定過濾器的情況下會發生的事情:一個intent加載了一個“singleTask ”的activity,初始化了一個新任務,用戶在這個任務中花費了一些時間來完成工作。然後用戶按下了HOME鍵。於是任務被要求轉至後臺並被主屏幕所掩蓋。因爲它並沒有在應用程序加載器中顯示圖標,這將導致用戶無法再返回它。

類似的困境也可由FLAG_ACTIVITY_NEW_TASK 標記引起。如果此標記使一個activity啓動了一個新任務繼而用戶按下了HOME鍵離開了它,則用戶必須要有一些方法再次回到這個任務。一些實體(諸如通知管理器)總是在另外的任務中啓動新activity,而不是做爲它們自己的一部分,所以它們總是將FLAG_ACTIVITY_NEW_TASK 標記包含在intent裏面並傳遞給startActivity() 。如果你寫了一個能被外部實體使用這個標記調用的activity,你必須注意要給用戶留一個返回這個被外部實體啓動的任務的方法。

當你不想讓用戶再次返回一個activity的情況下,可以將 <activity> 元素的 finishOnTaskLaunch 設置爲“true ”。參見前述 清理堆棧 。.

進程和線程

當一個應用程序開始運行它的第一個組件時,Android會爲它啓動一個Linux進程,並在其中執行一個單一的線程。默認情況下,應用程序所有的組件均在這個進程的這個線程中運行。

然而,你也可以安排組件在其他進程中運行,而且可以爲任意進程衍生出其它線程。

進程

組件運行所在的進程由manifest文件所控制。組件元素——<activity><service><receiver><provider> ——都有一個 process 屬性來指定組件應當運行於哪個進程之內。這些屬性可以設置爲使每個組件運行於它自己的進程之內,或一些組件共享一個進程而其餘的組件不這麼做。它們也可以 設置爲令不同應用程序的組件在一個進程中運行——使應用程序的組成部分共享同一個Linux用戶ID並賦以同樣的權限。<application> 元素也有一個process 屬性,以設定所有組件的默認值。

所有的組件實例都位於特定進程的主線程內,而對這些組件的系統調用也將由那個線程進行分發。一般不會爲每個實例創建線程。因此,某些方法總是運行在進程的主線程內,這些方法包括諸如View.onKeyDown() 這樣報告用戶動作以及後面 組件生命週期 一節所要討論的生命週期通告的。這意味着組件在被系統調用的時候,不應該施行長時間的抑或阻塞的操作(諸如網絡相關操作或是循環計算),因爲這將阻塞同樣位於這個進程的其它組件的運行。你應該如同下面線程 一節所敘述的那樣,爲這些長時間操作衍生出一個單獨的線程進行處理。

在可用內存不足而又有一個正在爲用戶進行服務的進程需要更多內存的時候,Android有時候可能會關閉一個進程。而在這個進程中運行着的應用程序也因此被銷燬。當再次出現需要它們進行處理的工作的時候,會爲這些組件重新創建進程。

在決定結束哪個進程的時候,Android會衡量它們對於用戶的相對重要性。比如說,相對於一個仍有用戶可見的activity的進程,它更有可能 去關閉一個其activity已經不爲用戶所見的進程。也可以說,決定是否關閉一個進程主要依據在那個進程中運行的組件的狀態。這些狀態將在後續的一節組件生命週期 中予以說明。

線程

儘管你可以把你的應用程序限制於一個單獨的進程中,有時,你仍然需要衍生出一個線程以處理後臺任務。因爲用戶界面必須非常及時的對用戶操作做出響 應,所以,控管activity的線程不應用於處理一些諸如網絡下載之類的耗時操作。所有不能在瞬間完成的任務都應安排到不同的線程中去。

線程在代碼中是以標準Java Thread 對象創建的。Android提供了很多便於管理線程的類: Looper 用於在一個線程中運行一個消息循環, Handler 用於處理消息,HandlerThread 用於使用一個消息循環啓用一個線程。

遠程過程調用

Android有一個輕量級的遠程過程調用(RPC)機制:即在本地調用一個方法,但在遠程(其它的進程中)進行處理,然後將結果返回調用者。這將 方法調用及其附屬的數據以系統可以理解的方式進行分離,並將其從本地進程和本地地址空間傳送至遠程過程和遠程地址空間,並在那裏重新裝配並對調用做出反 應。返回的結果將以相反的方向進行傳遞。Android提供了完成這些工作所需的所有的代碼,以使你可以集中精力來實現RPC接口本身。

RPC接口可以只包括方法。即便沒有返回值,所有方法仍以同步的方式執行(本地方法阻塞直至遠程方法結束)。

簡單的說,這套機制是這樣工作的:一開始,你用簡單的IDL(界面描繪語言)聲明一個你想要實現的RPC接口。然後用 aidl 工具爲這個聲明生成一個Java接口定義,這個定義必須對本地和遠程進程都可見。它包含兩個內部類,如下圖所示:

RPC mechanism.

內部類中有管理實現了你用IDL聲明的接口的遠程方法調用所需要的所有代碼。兩個內部類均實現了 IBinder 接口。一個用於系統在本地內部使用,你些的代碼可以忽略它;另外一個,我們稱爲Stub,擴展了Binder 類。除了實現了IPC調用的內部代碼之外,它還包括了你聲明的RPC接口中的方法的聲明。你應該如上圖所示的那樣寫一個Stub的子類來實現這些方法。

一般情況下,遠程過程是被一個服務所管理的(因爲服務可以通知系統關於進程以及它連接到別的進程的信息)。它包含着 aidl 工具產生的接口文件和實現了RPC方法的Stub的子類。而客戶端只需要包括aidl 工具產生的接口文件。

下面將說明服務與其客戶端之間的連接是如何建立的:

  • 服務的客戶端(位於本地)應該實現 onServiceConnected() onServiceDisconnected() 方法。這樣,當至遠程服務的連接成功建立或者斷開的時候,它們會收到通知。這樣它們就可以調用 bindService() 來設置連接。
  • 而服務則應該實現 onBind() 方法以接受或拒絕連接。這取決於它收到的intent(intent將傳遞給bindService() )。如果接受了連接,它會返回一個Stub的子類的實例。
  • 如果服務接受了連接,Android將會調用客戶端的onServiceConnected() 方法,並傳遞給它一個IBinder對象,它是由服務所管理的Stub的子類的代理。通過這個代理,客戶端可以對遠程服務進行調用。

線程安全方法

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

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

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

組件生命週期

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

Activity生命週期

一個activity主要有三個狀態:

  • 當在屏幕前臺時(位於當前任務堆棧的頂部),它是活躍運行 的狀態。它就是相應用戶操作的activity。
  • 當它失去焦點但仍然對用戶可見時,它處於暫停 狀態。即是:在它之上有另外一個activity。這個activity也許是透明的,或者未能完全遮蔽全屏,所以被暫停的activity仍對用戶可見。暫停的activity仍然是存活狀態 (它保留着所有的狀態和成員信息並連接至窗口管理器),但當系統處於極低內存的情況下,仍然可以殺死這個activity。

  • 如果它完全被另一個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() 以提交數據變化或準備停止與用戶的交互。

調用父類

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

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

  • 一個activity 完整的生命週期 自第一次調用 onCreate() 開始,直至調用onDestroy() 爲止。activity在onCreate() 中設置所有“全局”狀態以完成初始化,而在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在狀態間發生改變的時候,你進行操作所要實現的回調方法。

State diagram for an Android activity lifecycle.

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

方法 描述 可被殺死 下一個
onCreate() 在activity第一次被創建的時候調用。這裏是你做所有初始化設置的地方──創建視圖、綁定數據至列表等。如果曾經有狀態記錄(參閱後述Saving Activity State 。),則調用此方法時會傳入一個包含着此activity以前狀態的包對象做爲參數。

總繼之以onStart()

onStart()
     onRestart() 在activity停止後,在再次啓動之前被調用。

總繼之以onStart()

onStart()
onStart() 當activity正要變得爲用戶所見時被調用。

當activity轉向前臺時繼以onResume() ,在activity變爲隱藏時繼以onStop()

onResume()
or
onStop()
     onResume() 在activity開始與用戶進行交互之前被調用。此時activity位於堆棧頂部,並接受用戶輸入。

繼之以onPause()

onPause()
onPause() 當系統將要啓動另一個activity時調用。此方法主要用來將未保存的變化進行持久化,停止類似動畫這樣耗費CPU的動作等。這一切動作應該在短時間內完成,因爲下一個activity必須等到此方法返回後纔會繼續。

當activity重新回到前臺是繼以onResume() 。當activity變爲用戶不可見時繼以onStop()

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

如果activity再次回到前臺跟用戶交互則繼以onRestart() ,如果關閉activity則繼以onDestroy()

onRestart()
or
onDestroy()
onDestroy() 在activity銷燬前調用。這是activity接收的最後一個調用。這可能發生在activity結束(調用了它的 finish() 方法)或者因爲系統需要空間所以臨時的銷燬了此acitivity的實例時。你可以用isFinishing() 方法來區分這兩種情況。 nothing

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

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

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

保存activity狀態

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

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

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

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

協調activity

當一個activity啓動了另外一個的時候,它們都會經歷生命週期變化。一個會暫停乃至停止,而另一個則啓動。這種情況下,你可能需要協調好這些activity:

生命週期回調順序是已經定義好的,尤其是在兩個activity在同一個進程內的情況下:

  1. 調用當前activity的 onPause() 方法。
  2. 接着,順序調用新啓動activity的onCreate()onStart()onResume() 方法。
  3. 然後,如果啓動的activity不再於屏幕上可見,則調用它的onStop() 方法。

服務生命週期

服務以兩種方式使用:

  • 它可以啓動並運行,直至有人停止了它或它自己停止。在這種方式下,它以調用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 )

傳遞給bindService 的Intent的對象也會傳遞給onBind() 回調方法,而傳遞給unbindService() 的Intent對象同樣傳遞給onUnbind() 。如果服務允許綁定,onBind() 將返回一個供客戶端與服務進行交互的通訊渠道。如果有新的客戶端連接至服務,則onUnbind() 方法可以要求調用onRebind()

下圖描繪了服務的回調方法。儘管圖中對由startServicestartService 方法啓動的服務做了區分,但要記住,不論一個服務是怎麼啓動的,它都可能允許客戶端的連接,所以任何服務都可以接受onBind()onUnbind() 調用。

State diagram for Service callbacks.

廣播接收器生命週期

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

void onReceive(Context curContext , Intent broadcastMsg )

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

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

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

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

進程與生命週期

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

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

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

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

    • 它包含着一個不在前臺,但仍然爲用戶可見的activity(它的onPause() 方法被調用)。這種情況可能出現在以下情況:比如說,前臺activity是一個對話框,而之前的activity位於其下並可以看到。
    • 它包含了一個綁定至一個可視的activity的服務。

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

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

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

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

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

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

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

注:本人想翻譯的時候發現譯言已有,轉錄於此:http://www.yeeyan.com/articles/view/37503/34036

發佈了74 篇原創文章 · 獲贊 6 · 訪問量 42萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章