Android 應用程序模塊: 應用, 任務, 進程, 和線程 – Android移動開發技術文章_手機開發 Android移動開發教學課程

 

在大多數操作系統裡,存在獨立的一個1對1的可執行文件(如Windows裡的exe文件), 它可以產生進程,並能和界面圖標、應用進行用戶交互。但在Android裡,這是不固定的,理解將這些分散的部分如何進行組合是非常重要的。

 

由於Android這種可靈活變通的,在實現一個應用不同部分時你需要理解一些基礎技術:

 

一個android 包(簡稱.apk ) ,裡面包含應用程序的代碼以及資源。這是一個應用發佈,用戶能下載並安裝他們設備上的文件。

 

一個 任務 ,通常用戶能當它為一個“應用程序”來啟動:通常在桌面上會有一個圖標可以來啟動任務,這是一個上層的應用,可以將你的任務切換到前臺來。

 

一個 進程 是一個底層的代碼運行級別的核心進程。通常.apk包裡所有代碼運行在一個進程裡,一個進程對於一個.apk包;然而, 進程 標簽常用來改變代碼運行的位置,可以是 全部的.apk包 或者是獨立的 活動, 接收器, 服務, 或者 提供器組件。

 

任務

記住關鍵的一點:當用戶看到的“應用”,無論實際是如何處理的,它都是一個任務。如果你僅僅通過一些活動來創建一個.apk包,其中有一個肯定是上層入口(通過動作的intent-filter 以及分類android.intent.category.LAUNCHER),然後你的.apk包就創建瞭一個單獨任務,無論你啟動哪個活動都會是這個任務的一部分。

 

一個任務,從使用者的觀點,他是一個應用程序;對開發者來講,它是貫穿活動著任務的一個或者多個視圖,或者一個活動棧。當設置Intent.FLAG_ACTIVITY_NEW_TASK標志啟動一個活動意圖時,任務就被創建瞭;這個意圖被用作任務的根用途,定義區分哪個任務。如果活動啟動時沒有這個標記將被運行在同一個任務裡(除非你的活動以特殊模式被啟動,這個後面會討論)。如果你使用FLAG_ACTIVITY_NEW_TASK標記並且這個意圖的任務已經啟動,任務將被切換到前臺而不是重新加載。

 

FLAG_ACTIVITY_NEW_TASK必須小心使用:在用戶看來,一個新的應用程序由此啟動。如果這不是你期望的,你想要創建一個新的任務。另外,如果用戶需要從桌面退出到他原來的地方然後使用同樣的意圖打開一個新的任務,你需要使用新的任務標記。否則,如果用戶在你剛啟動的任務裡按桌面(HOME)鍵,而不是退出(BACK)鍵,你的任務以及任務的活動將被放在桌面程序的後面,沒有辦法再切換過去。

 

任務親和力(Affinities)

一些情況下Android需要知道哪個任務的活動附屬於一個特殊的任務,即使該任務還沒有被啟動。這通過任務親和力來完成,它為任務中一個或多個可能要運行的活動提供一個獨一無二的靜態名字。默認為活動命名的任務親和力的名字,就是實現該活動.apk包的名字。這提供一種通用的特性,對用戶來說,所有在.apk包裡的活動都是單一應用的一部分。

 

當不帶Intent.FLAG_ACTIVITY_NEW_TASK 標記啟動一個新的活動,任務親和力對新啟動的活動將沒有影響作用:它將一直運行在它啟動的那個任務裡。然而,如果使用NEW_TASK標記,親和力會檢測已經存在的任務是否具有相同的親和力。如果是,該任務會被切換到前臺,新的活動會在任務的最上面被啟動。

 

你可以在你的表現文件裡的應用程序標簽裡為.apk包裡所有的活動設置你自己的任務親和力,當然也可以為單獨的活動設置標簽。這裡有些例子演示如何使用:

 

如果你的.apk包裡包含多個用戶可啟動的上層應用程序,那麼你可能想要為每個活動分配不同的親和力。這裡有一個不錯的協定,你可以將不同的名字字串加上冒號附加在.apk包名字的後面 。 例如,"com.android.contacts"的親和力命名可以是"com.android.contacts:Dialer" and "com.android.contacts:ContactsList"。

如果你想替換一個通知,快捷鍵,或者其它能從外部啟動的應用程序的內部活動,你需要在你想替換的活動裡明確的設置任務親和力(taskAffinity)。例如,如果你想替換聯系人詳細信息瀏覽界面(用戶可以直接操作或者通過快捷方式調用),你需要設置任務親和力(taskAffinity)為“com.android.contacts”。

啟動模式以及啟動標記

你控制活動和任務通信的最主要的方法是通過設置啟動模式的屬性以及意圖相應的標記。這兩個參數能以不同的組合來共同控制活動的啟動結果,這在相應的文檔裡有描述。這裡我們隻描述一些通用的用法以及幾種不同的組合方式。

 

你最通常使用的模式是singleTop(除瞭默認為standard模式)。這不會對任務產生什麼影響;僅僅是防止在棧頂多次啟動同一個活動。

 

singleTask模式對任務有一些影響:它能使得活動總是在新的任務裡被打開(或者將已經打開的任務切換到前臺來)。使用這個模式需要加倍小心該進程是如何和系統其他部分交互的,它可能影響所有的活動。這個模式最好被用於應用程序入口活動的標記中。(支持MAIN活動和LAUNCHER分類)。

 

singleInstance啟動模式更加特殊,該模式隻能當整個應用隻有一個活動時使用。

 

有一種情況你會經常遇到,其它實體(如搜索管理器SearchManager 或者 通知管理器NotificationManager)會啟動你的活動。這種情況下,你需要使用Intent.FLAG_ACTIVITY_NEW_TASK 標記,因為活動在任務(這個應用/任務還沒有被啟動)之外被啟動。就像之前描述的一樣, 這種情況下標準特性就是當前和任務和新的活動的親和性匹配的任務將會切換到前臺,然後在最頂端啟動一個新的活動。當然,你也可以實現其它類型的特性。

 

一個常用的做法就是將Intent.FLAG_ACTIVITY_CLEAR_TOP 和NEW_TASK一起使用。這樣做,如果你的任務已經處於運行中,任務將會被切換到前臺來, 在棧裡的所有的活動除瞭根活動,都將被清空,根活動的onNewIntent(Intent) 方法傳入意圖參數後被調用。當使用這種方法的時候singleTop 或者singleTask啟動模式經常被使用,這樣當前實例會被置入一個新的意圖,而不是銷毀原先的任務然後啟動一個新的實例。

 

另外你可以使用的一個方法是設置活動的任務親和力為空字串(表示沒有親和力),然後設置finishOnBackground屬性。 如果你想讓用戶給你提供一個單獨的活動描述的通知,倒不如返回到應用的任務裡,這個比較管用。要指定這個屬性,不管用戶使用BACK還是HOME,活動都會結束;如果這個屬性沒有指定,按HOME鍵將會導致活動以及任務還留在系統裡,並且沒有辦法返回到該任務裡。

 

請確保閱讀過文檔啟動模式屬性(launchMode attribute) 以及 意圖標記(Intent flags) ,關註這些選項的詳細信息。

 

進程

在Android中,進程是應用程序的完整實現,而不是用戶通常瞭解的那樣。他們主要用途很簡單:

 

提高穩定性和安全性,將不信任或者不穩定的代碼移動到其他進程。

可將多個.apk包運行在同一個進程裡減少系統開銷。

幫助系統管理資源,將重要的代碼放在一個單獨的進程裡,這樣就可以單獨銷毀應用程序的其他部分。

像前面描述的一樣,進程的屬性被用來控制那些有特殊應用組件運行的進程。註意這個屬性不能違反系統安全: 如果兩個.apk包不能共享同一個用戶ID,卻試圖運行在通一個進程裡,這種情況是不被允許的,事實上系統將會創建兩個不同的進程。

 

請查看安全相關文檔以獲取更多關於安全限制方面的信息。

 

線程

每個進程包含一個或多個線程。多數情況下,Android 避免在進程裡創建多餘的線程,除非它創建它自己的線程,我們應保持應用程序的單線程性。 一個重要的結論就是所有呼叫實例, 廣播接收器, 以及 服務的實例都是由這個進程裡運行的主線程創建的。

 

註意新的線程不是為活動,廣播接收器,服務或者內容提供器實例創建:這些應用程序的組件在進程裡被實例化(除非另有說明,都在同一個進程處理),實際上是進程的主線程。這說明當系統調用時這些組件(包括服務)不需要進程遠距離或者封鎖操作(就像網絡呼叫或者計算循環),因為這將阻止進程中的所有其他組件。你可以使用標準的線程 類或者Android的HandlerThread 類去對其它線程執行遠程操作。

 

這裡有一些關於創建線程規則的例外:

 

呼叫IBinder或者IBinder實現的接口,如果該呼叫來自其他進程,你可以通過線程發送的IBinder或者本地進程中的線程池呼叫它們,從進程的主線程呼叫是不可以的。特殊情況下,,呼叫一個服務 的IBinder可以這樣處理。(雖然在服務裡呼叫方法在主線程裡已經完成。)這意味著IBinder接口的實現必須要有一種線程安全的方法,這樣任意線程才能同時訪問它。

 

呼叫由正在被調用的線程或者主線程以及IBinder派發的內容提供器 的主方法。被指定的方法在內容提供器的類裡有記錄。這意味著實現這些方法必須要有一種線程安全的模式,這樣任意其它線程同時可以訪問它。

 

呼叫視圖以及由視圖裡正在運行的線程組成的子類。通常情況下,這會被作為進程的主線程,如果你創建一個線程並顯示一個窗口,那麼繼承的窗口視圖將從那個線程裡啟動。  

摘自 chaoqunz的博客

發佈留言