前言html
本章內容爲開發者指南(Dev Guide)/Framework Topics/Application Fundamentals,版本爲Android2.3 r1,翻譯轉載並整理自譯言:"biAji",原文地址:"http://article.yeeyan.org/view/37503/34036",再次感謝"bjAji" !期待你一塊兒參與翻譯Android的相關資料,聯繫我over140@gmail.com。
java
聲明android
本文檔轉載並整理自譯言:Android開發指南 1──應用程序基礎數據庫
Android中文翻譯組:http://goo.gl/6vJQl
瀏覽器
原文
網絡
正文異步
應用程序基礎(Application Fundamentals)ide
Android應用程序使用Java作爲開發語言。aapt工具把編譯後的Java代碼連同其它應用程序須要的數據和資源文件一塊兒打包到一個Android包文件中,這個文件使用.apk作爲擴展名,它是分發應用程序並安裝到移動設備的媒介,用戶只需下載並安裝此文件到他們的設備。單一.apk文件中的全部代碼被認爲是一個應用程序。工具
從不少方面來看,每一個Android應用程序都存在於它本身的世界之中:
* 默認狀況下,每一個應用程序均運行於它本身的Linux進程中。當應用程序中的任意代碼開始執行時,Android啓動一個進程,而當再也不須要此進程而其它應用程序又須要系統資源時,則關閉這個進程。
* 每一個進程都運行於本身的Java虛擬機(VM)中。因此應用程序代碼實際上與其它應用程序的代碼是隔絕的。
* 默認狀況下,每一個應用程序均被賦予一個惟一的Linux用戶ID,並加以權限設置,使得應用程序的文件僅對這個用戶、這個應用程序可見。固然,也有其它的方法使得這些文件一樣能爲別的應用程序所訪問。
使兩個應用程序共有同一個用戶ID是可行的,這種狀況下他們能夠看到彼此的文件。從系統資源維護的角度來看,擁有同一個ID的應用程序也將在運行時使用同一個Linux進程,以及同一個虛擬機。
應用程序組件(Application Components)
Android的核心功能之一就是一個應用程序可使用其它應用程序的元素(若是那個應用程序容許的話)。好比說,若是你的應用程序須要一個圖片捲動列 表,而另外一個應用程序已經開發了一個合用的而又容許別人使用的話,你能夠直接調用那個捲動列表來完成工做,而不用本身再開發一個。你的應用程序並無吸納 或連接其它應用程序的代碼,它只是在有需求的時候啓動了其它應用程序的那個功能部分。
爲達到這個目的,系統必須在一個應用程序的一部分被須要時啓動這個應用程序,並將那個部分的Java對象實例化。與在其它系統上的應用程序不一樣,Android應用程序沒有爲應用準備一個單獨的程序入口(好比說,沒有main()
方法), 而是爲系統依照需求實例化提供了基本的組件。共有四種組件類型:
Activities
* Activity是爲用戶操做而展現的可視化用戶界面。好比說,一個activity能夠展現一個菜單項列表供用戶選擇,或者顯示一些包含說明的照片。一個短消息應用程序能夠包括一個用於顯示作爲發送對象的聯繫人的列表的activity,一個給選定的聯繫人寫短信的activity以及翻閱之前的短信和改變設置的activity。儘管它們一塊兒組成了一個內聚的用戶界面,但其中每一個activity都與其它的保持獨立。每一個都是以Activity類爲基類的子類實現。
* 一個應用程序能夠只有一個activity,或者,如剛纔提到的短信應用程序那樣,包含不少個。每一個activity的做用,以及其數目,天然取決於應用 程序及其設計。通常狀況下,總有一個應用程序被標記爲用戶在應用程序啓動的時候第一個看到的。從一個activity轉向另外一個的方式是靠當前的 activity啓動下一個。
* 每一個activity都被給予一個默認的窗口以進行繪製。通常狀況下,這個窗口是滿屏的,但它也能夠是一個小的位於其它窗口之上的浮動窗口。一個 activity也可使用超過一個的窗口──好比,在activity運行過程當中彈出的一個供用戶反應的小對話框,或是當用戶選擇了屏幕上特定項目後顯 示的必要信息。
* 窗口顯示的可視內容是由一系列視圖構成的,這些視圖均繼承自 View
基類。每一個視圖均控制着窗口中一塊特定的矩形空 間。父級視圖包含並組織它子視圖的佈局。葉節點視圖(位於視圖層次最底端)在它們控制的矩形中進行繪製,並對用戶對其直接操做作出響應。因此,視圖是 activity與用戶進行交互的界面。好比說,視圖能夠顯示一個小圖片,並在用戶指點它的時候產生動做。Android有不少既定的視圖供用戶直接使 用,包括按鈕、文本域、卷軸、菜單項、複選框等等。
* 視圖層次是由Activity.setContentView()
方法放入activity的窗口之中的。上下文視圖是位於視圖層次根位置的視圖對象。(參見用戶界面章節獲取關於視圖及層次的更多信息。)
服務(Services)
* 服務沒有可視化的用戶界面,而是在一段時間內在後臺運行。好比說,一個服務能夠在用戶作其它事情的時候在後臺播放背景音樂、從網絡上獲取一些數據或者計算一些東西並提供給須要這個運算結果的activity使用。每一個服務都繼承自Service基類。
* 一個媒體播放器播放播放列表中的曲目是一個不錯的例子。播放器應用程序可能有一個或多個activity來給用戶選擇歌曲並進行播放。然而,音樂播放這個 任務自己不該該爲任何activity所處理,由於用戶指望在他們離開播放器應用程序而開始作別的事情時,音樂仍在繼續播放。爲達到這個目的,媒體播放器 activity應該啓用一個運行於後臺的服務。而系統將在這個activity再也不顯示於屏幕以後,仍維持音樂播放服務的運行。
* 你能夠鏈接至(綁定)一個正在運行的服務(若是服務沒有運行,則啓動之)。鏈接以後,你能夠經過那個服務暴露出來的接口與服務進行通信。對於音樂服務來講,這個接口能夠容許用戶暫停、回退、中止以及從新開始播放。
* 如同activity和其它組件同樣,服務運行於應用程序進程的主線程內。因此它不會對其它組件或用戶界面有任何干擾,它們通常會派生一個新線程來進行一些耗時任務(好比音樂回放)。參見下述 進程和線程(Processes and Threads) 。
廣播接收器(Broadcast receivers)
* 廣播接收器是一個專一於接收廣播通知信息,並作出對應處理的組件。不少廣播是源自於系統代碼的──好比,通知時區改變、電池電量低、拍攝了一張照片或者用戶改變了語言選項。應用程序也能夠進行廣播──好比說,通知其它應用程序一些數據下載完成並處於可用狀態。
* 應用程序能夠擁有任意數量的廣播接收器以對全部它感興趣的通知信息予以響應。全部的接收器均繼承自BroadcastReceiver基類。
* 廣播接收器沒有用戶界面。然而,它們能夠啓動一個activity來響應它們收到的信息,或者用NotificationManager來通知用戶。通知能夠用不少種方式來吸引用戶的注意力──閃動背燈、震動、播放聲音等等。通常來講是在狀態欄上放一個持久的圖標,用戶能夠打開它並獲取消息。
內容提供者(Content providers)
* 內容提供者將一些特定的應用程序數據供給其它應用程序使用。數據能夠存儲於文件系統、SQLite數據庫或其它方式。內容提供者繼承於ContentProvider 基類,爲其它應用程序取用和存儲它管理的數據實現了一套標準方法。然而,應用程序並不直接調用這些方法,而是使用一個 ContentResolver 對象,調用它的方法做爲替代。ContentResolver能夠與任意內容提供者進行會話,與其合做來對全部相關交互通信進行管理。
* 參閱獨立的內容提供者Content Providers 章節得到更多關於使用內容提供者的內容。
每當出現一個須要被特定組件處理的請求時,Android會確保那個組件的應用程序進程處於運行狀態,或在必要的時候啓動它。並確保那個相應組件的實例的存在,必要時會建立那個實例。
激活組件Activating components: intents
當接收到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() 來創建鏈接,而後調用服務中定義的對象來影響回放。
後面一節:遠程方法調用(Remote procedure calls)將更詳細的闡明如何綁定至服務。
* 應用程序能夠憑藉將Intent對象傳遞給 Context.sendBroadcast()
,Context.sendOrderedBroadcast()
, 以及Context.sendStickyBroadcast()
和其它相似方法來產生一個廣播。Android會調用全部對此廣播有興趣的廣播接收器的 onReceive()
方法,將intent傳遞給它們。
欲瞭解更多intent消息的信息,請參閱獨立章節 Intent和Intent濾過器(Intents and Intent Filters)。
關閉組件(Shutting down components)
內容提供者僅在響應ContentResolver提出請求的時候激活。而一個廣播接收器僅在響應廣播信息的時候激活。因此,沒有必要去顯式的關閉這些組件。
而activity則不一樣,它提供了用戶界面,並與用戶進行會話。因此只要會話依然持續,哪怕對話過程暫時停頓,它都會一直保持激活狀態。與此類似,服務也會在很長一段時間內保持運行。因此Android爲關閉activity和服務提供了一系列的方法。
* 能夠經過調用它的finish()
方法來關閉一個activity。一個activity能夠經過調用另一個activity(它用startActivityForResult() 啓動
的)的finishActivity()
方法來關閉它。
* 服務能夠經過調用它的stopSelf()
方法來中止,或者調用 Context.stopService()
。
系統也會在組件再也不被使用的時候或者Android須要爲活動組件聲明更多內存的時候關閉它。後面的組件的生命週期一節,將對這種可能及附屬狀況進行更詳細的討論。
manifest文件(The manifest file)
當Android啓動一個應用程序組件以前,它必須知道那個組件是存在的。因此,應用程序會在一個manifest文件中聲明它的組件,這個文件會被打包到Android包中。這個.apk文件還將涵括應用程序的代碼、文件以及其它資源。
這個manifest文件以XML做爲結構格式,並且對於全部應用程序,都叫作AndroidManifest.xml。爲聲明一個應用程序組件,它還會 作不少額外工做,好比指明應用程序所需連接到的庫的名稱(除了默認的Android庫以外)以及聲明應用程序指望得到的各類權限。
但manifest文件的主要功能仍然是向Android聲明應用程序的組件。舉例說明,一個activity能夠以下聲明:
<activity>
元素的name屬性指定了實現了這個activity的 Activity的子類。icon和label屬性指向了包含展現給用戶的此activity的圖標和標籤的資源文件。
其它組件也以相似的方法聲明──<service>
元素用於聲明服務, <receiver>
元素用於聲明廣播接收器,而 <provider>
元素用於聲明內容提供者。 manifest文件中未進行聲明的activity、服務以及內容提供者將不爲系統所見,從而也就不會被運行。然而,廣播接收器既能夠在manifest文件中聲明,也能夠在代碼中進行動態的建立,並以調用Context.registerReceiver()
的方式註冊至系統。
欲更多瞭解如何爲你的應用程序構建manifest文件,請參閱AndroidManifest.xml文件一章。
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激活。
對於在代碼中建立並註冊的廣播接收器來講,intent過濾器將被直接以 IntentFilter對象實例化。其它過濾器則在manifest文件中設置。
欲得到更多intent過濾器的信息,請參閱獨立章節: 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顯示了出來。
上述的種種便是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
接下來的一節會描述這些標記以及屬性的做用,它們是如何互相影響的,以及控制它們的使用時必須考慮到的因素。
任務共用性和新任務Affinities and new tasks
默認狀況下,一個應用程序中的activity相互之間會有一種Affinity──也就是說,它們首選都歸屬於一個任務。然而,能夠在<
activity>
元素中把每一個
activity的
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)
<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>
元素的描述。
清理堆棧(Clearing the stack)
若是用戶離開一個任務很長一段時間,系統會清理該任務中除了根activity以外的全部activity。當用戶再次回到這個任務的時候,除了只剩下初 始化activity尚存以外,其他都跟用戶上次離開它的時候同樣。這樣作的緣由是:在一段時間以後,用戶再次回到一個任務的時候,他們更指望放棄他們之 前的所做所爲,作些新的事情。
這些屬於默認行爲,另外,也存在一些activity的屬性用以控制並改變這些行爲:
若是一個任務的根activity中此屬性設置爲「true」,則上述默認行爲不會發生。任務將在很長的一段時間內保留它堆棧內的全部activity。
若是一個任務的根activity中此屬性設置爲「true」,則每當用戶離開這個任務和返回它的時候,堆棧都會被清空至只留下rootactivity。換句話說,這是alwaysRetainTaskState的另外一個極端。哪怕僅是過了一小會兒,用戶回到任務時,也是見到它的初始狀態。
這個屬性與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_TOP與FLAG_ACTIVITY_NEW_TASK常常合併使用。這時,這些標記提供了一種定位其它任務中現存的activity並將它們置於能夠對intent作出響應的位置的方法。
啓動任務(Starting tasks)
當一個activity被指定一個「android.intent.action.MAIN」作爲動做,以及「android.intent.category.LAUNCHER」作爲類別的intent過濾器以後(在前述intent過濾器一節中已經有了這個示例),它就被設置爲一個任務的入口點。這樣的過濾器設置會在應用程序加載器中爲此activity顯示一個圖標和標籤,以供用戶加載任務或加載以後在任意時間回到這個任務。
第二個能力至關重要:用戶必須能夠離開一個任務,並在一段時間後返回它。出於這個考慮,加載模式被設定爲「singleTask」和「singleInstance」的activity老是會初始化一個新任務,這樣的activity僅能用於指定了一個MAIN和LAUNCHER過濾器的狀況之下。咱們來舉例說明若是沒指定過濾器的狀況下會發生的事情:一個intent加載了一個「singleTask」的activity,初始化了一個新任務,用戶在這個任務中花費了一些時間來完成工做。而後用戶按下了HOME鍵。因而任務被要求轉至後臺並被主屏幕所掩蓋。由於它並無在應用程序加載器中顯示圖標,這將致使用戶沒法再返回它。
相似的困境也可由FLAG_ACTIVITY_NEW_TASK標記引發。若是此標記使一個activity啓動了一個新任務繼而用戶按下了HOME鍵離開了它,則用戶必需要有一些方法再次回到這個任務。一些實體(諸如通知管理器)老是在另外的任務中啓動新activity,而不是作爲它們本身的一部分,因此它們老是將FLAG_ACTIVITY_NEW_TASK標記包含在intent裏面並傳遞給startActivity()。若是你寫了一個能被外部實體使用這個標記調用的activity,你必須注意要給用戶留一個返回這個被外部實體啓動的任務的方法。
當你不想讓用戶再次返回一個activity的狀況下,能夠將 <
activity>
元素的 finishOnTaskLaunch設置爲「true」。參見前述清理堆棧。
進程和線程(Processes and Threads)
當一個應用程序開始運行它的第一個組件時,Android會爲它啓動一個Linux進程,並在其中執行一個單一的線程。默認狀況下,應用程序全部的組件均在這個進程的這個線程中運行。
然而,你也能夠安排組件在其餘進程中運行,並且能夠爲任意進程衍生出其它線程。
進程(Processes)
組件運行所在的進程由manifest文件所控制。組件元素——<
activity>
, <
service>
, <
receiver>
和<
provider>
——都有一個 process 屬性來指定組件應當運行於哪一個進程以內。這些屬性能夠設置爲使每一個組件運行於它本身的進程以內,或一些組件共享一個進程而其他的組件不這麼作。它們也能夠 設置爲令不一樣應用程序的組件在一個進程中運行——使應用程序的組成部分共享同一個Linux用戶ID並賦以一樣的權限。<
application>
元素也有一個process屬性,以設定全部組件的默認值。
全部的組件實例都位於特定進程的主線程內,而對這些組件的系統調用也將由那個線程進行分發。通常不會爲每一個實例建立線程。所以,某些方法老是運行在進程的主線程內,這些方法包括諸如View.onKeyDown()
這樣報告用戶動做以及後面 組件生命週期一節所要討論的生命週期通告的。這意味着組件在被系統調用的時候,不該該施行長時間的抑或阻塞的操做(諸如網絡相關操做或是循環計算),由於這將阻塞一樣位於這個進程的其它組件的運行。你應該如同下面線程一節所敘述的那樣,爲這些長時間操做衍生出一個單獨的線程進行處理。
在可用內存不足而又有一個正在爲用戶進行服務的進程須要更多內存的時候,Android有時候可能會關閉一個進程。而在這個進程中運行着的應用程序也所以被銷燬。當再次出現須要它們進行處理的工做的時候,會爲這些組件從新建立進程。
在決定結束哪一個進程的時候,Android會衡量它們對於用戶的相對重要性。好比說,相對於一個仍有用戶可見的activity的進程,它更有可能去關閉 一個其activity已經不爲用戶所見的進程。也能夠說,決定是否關閉一個進程主要依據在那個進程中運行的組件的狀態。這些狀態將在後續的一節組件生命週期中予以說明。
線程(Threads)
儘管你能夠把你的應用程序限制於一個單獨的進程中,有時,你仍然須要衍生出一個線程以處理後臺任務。由於用戶界面必須很是及時的對用戶操做作出響應,所 以,控管activity的線程不該用於處理一些諸如網絡下載之類的耗時操做。全部不能在瞬間完成的任務都應安排到不一樣的線程中去。
線程在代碼中是以標準Java Thread對象建立的。Android提供了不少便於管理線程的類: Looper用於在一個線程中運行一個消息循環, Handler用於處理消息,HandlerThread 用於使用一個消息循環啓用一個線程。
遠程方法調用(Remote procedure calls)
Android有一個輕量級的遠程方法調用(RPC)機制:即在本地調用一個方法,但在遠程(其它的進程中)進行處理,而後將結果返回調用者。這將方法調用及其附屬的數據以系統能夠理解的方式進行分離,並將其從本地進程和本地地址空間傳送至遠程過程和遠程地址空間,並在那裏從新裝配並對調用作出反應。返回 的結果將以相反的方向進行傳遞。Android提供了完成這些工做所需的全部的代碼,以使你能夠集中精力來實現RPC接口自己。
RPC接口能夠只包括方法。即使沒有返回值,全部方法仍以同步的方式執行(本地方法阻塞直至遠程方法結束)。
簡單的說,這套機制是這樣工做的:一開始,你用簡單的IDL(界面描繪語言)聲明一個你想要實現的RPC接口。而後用 aidl
工具爲這個聲明生成一個Java接口定義,這個定義必須對本地和遠程進程均可見。它包含兩個內部類,以下圖所示:
內部類中有管理實現了你用IDL聲明的接口的遠程方法調用所須要的全部代碼。兩個內部類均實現了 IBinder接口。一個用於系統在本地內部使用,你些的代碼能夠忽略它;另一個,咱們稱爲Stub,擴展了Binder類。除了實現了IPC調用的內部代碼以外,它還包括了你聲明的RPC接口中的方法的聲明。你應該如上圖所示的那樣寫一個Stub的子類來實現這些方法。
通常狀況下,遠程過程是被一個服務所管理的(由於服務能夠通知系統關於進程以及它鏈接到別的進程的信息)。它包含着 aidl工具產生的接口文件和實現了RPC方法的Stub的子類。而客戶端只須要包括aidl工具產生的接口文件。
下面將說明服務與其客戶端之間的鏈接是如何創建的:
* 服務的客戶端(位於本地)應該實現 onServiceConnected()
和 onServiceDisconnected()
方法。這樣,當至遠程服務的鏈接成功創建或者斷開的時候,它們會收到通知。這樣它們就能夠調用 bindService()
來設置鏈接。
* 而服務則應該實現 onBind()
方法以接受或拒絕鏈接。這取決於它收到的intent(intent將傳遞給bindService())。若是接受了鏈接,它會返回一個Stub的子類的實例。
* 若是服務接受了鏈接,Android將會調用客戶端的onServiceConnected() 方法,並傳遞給它一個IBinder對象,它是由服務所管理的Stub的子類的代理。經過這個代理,客戶端能夠對遠程服務進行調用。