第一種解釋:
進程
在大多數其餘平臺的開發中,每一個開發人員對本身應用的進程模型都有很是清晰的瞭解。好比,一個控制檯程序,你能夠想見它從main函數開始啓動一個進程,到 main函數結束,進程執行完成退出;在UI程序中,每每是有一個消息循環在跑,當接受到Exit消息後,退出消息循環結束進程。在該程序運行過程當中,啓動了什麼進程,和第三方進程進行通訊等等操做,每一個開發者都是心如明鏡一本賬算得清清楚楚。進程邊界,在這裏,猶如國界通常,每一次穿越都會留下深深的印跡。
在Android程序中,開發人員能夠直接感知的,每每是Task而已。倍感清晰的,是組件邊界,而進程邊界變得難以琢磨,甚至有了進程託管一說。Android中不但剝奪了手工鍛造內存權力,連手工處置進程的權責,也絕不猶豫的獨佔了。
固然,Android隱藏進程細節,並非刻意爲之,而是天然而然水到渠成的。若是,咱們把傳統的應用稱爲面向進程的開發,那麼,在Android中,咱們作得就是面向組件的開發。從前面的內容能夠知道,Android組件間的跳轉和通訊,都是在第三方介入的前提下進行,正因爲這種介入,使得兩個組件通常不會直接發生聯繫(於Service的通訊,是不須要第三方介入的,所以Android把它所有假設成爲穿越進程邊界,統一基於RPC來通訊,這樣,也是爲了掩蓋進程細節...),其中是否穿越進程邊界也就變得不重要。所以,若是這時候,還須要開發者關注進程,就會變得很奇怪,很費解,乾脆,Android將全部的進程一併託管去了,上層無須知道進程的生死和通訊細節。
在Android的底層,進程構造了底部的一個運行池,不只僅是Task中的各個Activity組件,其餘三大組件Service、Content Provider、Broadcast Receiver,都是寄宿在底層某個進程中,進行運轉。在這裏,進程更像一個資源池(概念形如線程池,上層要用的時候取一個出來就好,而不關注具體取了哪個...),只是爲了承載各個組件的運行,而各個組件直接的邏輯關係,它們並不關心。但咱們能夠想象,爲了保證總體性,在默認狀況下,Android確定傾向於將同一Task、同一應用的各個組件扔進同一個進程內,可是固然,出於效率考慮,Android也是容許開發者進行配置。
在Android中,總體的<application>(將影響其中各個組件...)和底下各個組件,均可以設置<process>屬性,相同<process>屬性的組件將扔到同一個進程中運行。最多見的使用場景,是經過配置<application>的process屬性,將不一樣的相關應用,塞進一個進程,使得它們能夠同生共死。還有就是將常常和某個Service組件進行通訊的組件,放入同一個進程,由於與Service通訊是個密集操做,走的是RPC,開銷不小,經過配置,能夠變成進程內的直接引用,消耗頗小。
除了經過<process>屬性,不一樣的組件還有一些特殊的配置項,以Content Provider爲例(經過<provider>項進行配置...)。<provider>項有一個mutiprocess的屬性,默認值爲false,這意味着Content Provider,僅會在提供該組件的應用所在進程構造一個實例,第三方想使用就須要經由RPC傳輸數據。這種模式,對於構造開銷大,數據傳輸開銷小的場合是很是適用的,而且可能提升緩存的效果。可是,若是是數據傳輸很大,抑或是但願在此提升傳輸的效率,就須要將mutiprocess設置成true,這樣,Content Provider就會在每個調用它的進程中構造一個實例,避免進程通訊的開銷。
既然,是Android系統幫助開發人員託管了進程,那麼就須要有一整套紛繁的算法去執行回收邏輯。Android中各個進程的生死,和運行在其中的各個組件有着密切的聯繫,進程們依照其上組件的特色,被排入一個優先級體系,在須要回收時,從低優先級到高優先級回收。Android進程共分爲五類優先級,分別是:Foreground Process, Visible Process, Service Process, Background Process, Empty Process。顧名思義不難看出,這說明,越和用戶操做緊密相連的,越是正與用戶交互的,優先級越高,越難被回收。具體詳情,參見:guide/topics/fundamentals.html#proclife。
有了優先級,還須要有良好的回收時機。回收太早,緩存命中機率低可能引發不斷的創造進程銷燬進程,池的優點蕩然無存;回收的太晚,總體開銷大,系統運行效率下降,好端端的法拉利可能被糟蹋成一枚QQ老爺車。Android的進程回收,最重要的是考量內存開銷,以及電量等其餘資源情況,此外每一個進程承載的組件數量、單個應用開闢的進程數量等數量指標,也是做爲衡量的一個重要標識。另外,一些運行時的時間開銷,也被嚴格監控,啓動慢的進程會很被強行kill掉。Android會定時檢查上述參數,也會在一些極可能發生進程回收的時間點,好比某個組件執行完成後,來作回收的嘗試。
從用戶體驗角度來看,Android的進程機制,會有很可喜的一面,有的程序啓動速度很慢,可是在資源充沛的前提下,你反覆的退出再使用,則啓動變得極其快速(進程沒死,只是從後臺弄到了前臺),這就是拜進程託管所賜的。固然,可喜的另外一面就是可悲了,Android的託管算法,還時不時的展示其幼稚的一面,明明用戶已經明顯感受到操做系統運行速度降低了,打開任務管理器一看,一票應用還生龍活虎的跳躍着,必需要手動幫助它們終結生命找到墳墓,這使得任務管理器基本成爲Android的裝機必備軟件。
從開發角度上來看,Android這套進程機制,解放了開發者的手腳。開發人員不須要處心積慮的構造一個後臺進程偷偷默默監聽某個時間,並嘗試用各類各樣的守護手段,把本身的進程鍛造的猶如不死鳥一輝通常,進程生死的問題,已經原理了普通開發人員須要管理的範疇內。但同時,於GC和人肉內存管理的爭議同樣,全部開發人員都不相信算法能比本身作得效率更高更出色。但我一直堅信一點,全部效率的優點都會隨着算法的不斷改良硬件的不斷提高而消失殆盡,只有開發模式的簡潔不會隨時間而有任何變化。
組件生命週期
任何架構上的變化,都會引發上層開發模式的變化,Android的進程模型,雖然使開發者再也不須要密切關注進程的建立和銷燬的時機,但仍然須要關注這些時間點對組件的影響。好比,你可能須要在進程銷燬以前,將寫到內存上的內容,持久化到硬盤上,這就須要關注進程退出前發生的一些事件。
在Android中,把握這些時間點,就必須瞭解組件生命週期(Components Lifecycles)。所謂組件的生命在週期,就是在組件在先後臺切換、被用戶建立退出、被系統回收等等事件發生的時候,會有一些事件通知到對應組件上,開發人員能夠選擇性的處理這些事件在對應的時間點上來完成一些附加工做。
除Content Provider,其餘組件都會有生命週期的概念,都須要依照這個模型定時定點處理一些情況,所有內容參見:guide/topics/fundamentals.html#lcycles。在這裏,擒賊先擒王,仍是拿Activity出來做楷模。
繼續偷圖,來自SDK。一個天然的Activity生命旅途,從onCreate開始,到onDestroy消亡。但月有陰晴圓缺組件有禍福旦夕,在系統須要的時候且組件位於後臺時,所在的進程隨時可能爲國捐軀被回收,這就使得知道切入後臺這個事情也變得很重要。
當組件進入棧頂,與用戶開始交互,會調用onResume函數,相似,當退出棧頂,會有onPause函數被呼喚。onResume和onPause能夠處理不少事情,最常規的,就是作一些文件或設置項的讀寫工做。由於,在該組件再也不前臺運行的時候,可能別的組件會須要讀寫一樣一份文件和設置,若是再也不onResume作刷新工做,用的可能就是一份髒數據了(固然,具體狀況,還須要具體分析,若是文件不會被多頭讀寫,能夠放到onCreate裏面去作讀工做)。
除了前述切入後臺會被其餘組件騷擾的問題,另外,死無定因也是件很可怕的事情。在Android中,組件都有兩種常見的死法,一種是天然消亡,好比,棧元素ABC,變成AB了,C組件就天然消亡了。這種死發輕如鴻毛,不須要額外關心。但另外一種狀況,就是被系統回收,那是死的重如泰山,爲國捐軀嘛。
但這種捐軀的死法,對用戶來講,比較費解。想象一下,一款遊戲,不能存盤,你一直玩啊玩,三天三夜沒閤眼,這時候你mm打來電話鼓勵一下,你精神抖擻的準備再接再礪,卻發現你的遊戲進程,在切入後臺以後,被系統回收了,一晚上回到解放前三天努力成爲一場泡影,你會不會想殺作遊戲的人,會不會會不會會不會,必定會嘛。這時候,若是沒有Activity生命週期這碼事,遊戲程序員必定是被冤死的,成了Android的替罪羊。可是,Android的組件是有生命週期的, 若是真的發生這樣狀況,不要猶豫,去殺開發的程序員吧。
爲了逃生,程序員們有一塊免死金牌,那就是Android的state機制。所謂state,就是開發人員將一些當前運行的狀態信息存放在一個Bundle對象裏面,這是一個可序列化鍵值對集合。若是該Activity組件所處的進程須要回收,Android核心會將其上Activity組件的Bundle對象持久化到磁盤上,當用戶回到該Activity時候,系統會從新構造該組件,並將持久化到磁盤上的Bundle對象恢復。有了這樣的持久化的狀態信息,開發人員能夠很好的區分具體死法,並有機會的使得死而復生的Activity恢復到死前狀態。開發者應該作的,是經過onSaveInstanceState函數把須要維繫的狀態信息(在默認的狀態下,系統控件都會本身保存相關的狀態信息,好比TextView,會保存當前的Text信息,這都不須要開發人員擔憂...),寫入到Bundle對象,而後在onRestoreInstanceState函數中讀取並恢復相關信息(onCreate,onStart,也均可以處理...)。
線程
讀取數據,後臺處理,這些猥瑣的夥計,天然少不了線程的參與。在Android核心的調度層面,是不屑於考量線程的,它關注的只有進程,每個組件的構造和處理,都是在進程的主線程上作的,這樣能夠保證邏輯的足夠簡單。多線程,每每都是開發人員須要作的。
Android的線程,也是經過派生Java的Thread對象,實現Run方法來實現的。但當用戶須要跑一個具備消息循環的線程的時候,Android有更好的支持,來自於Handler和Looper。Handler作的是消息的傳送和分發,派生其handleMessage函數,能夠處理各類收到的消息,和win開發無異。Looper的任務,則是構造循環,等候退出或其餘消息的來臨。在Looper的SDK頁面,有一個消息循環線程實現的標準範例,固然,更爲標準的方式也許是構造一個HandlerThread線程,將它的Looper傳遞給Handler。
在Android中,Content Provider的使用,每每和線程掛鉤,誰讓它和數據相關呢。在前面提到過,Content Provider爲了保持更多的靈活性,自己只提供了同步調用的接口,而因爲異步對Content Provider進行增刪改查是一個常作操做,Android經過AsyncQueryHandler對象,提供了異步接口。這是一個Handler的子類,開發人員能夠調用startXXX方法發起操做,經過派生onXXXComplete方法,等待執行完畢後的回調,從而完成整個異步調用的流程,十分的簡約明瞭。
實現
整個任務、進程管理的核心實現,盡在ActivityManagerService中。上一篇說到,Intent解析,就是這個ActivityManagerService來負責的,其實,它是一個很名存實亡的類,由於雖然名爲Activity的Manager Service,但它管轄的範圍,不僅是Activity,還有其餘三類組件,和它們所在的進程。
在ActivityManagerService中,有兩類數據結構最爲醒目,一個是ArrayList,另外一個是HashMap。 ActivityManagerService有大量的ArrayList,每個組件,會有多個ArrayList來分狀態存放。調度工做,每每就是從一個ArrayList裏面拿出來,找個方法調一調,而後扔到另外一個ArrayList裏面去,當這個組件沒對應的ArrayList放着的時候,說明它離死不遠了。HashMap,是由於有組件是須要用名字或Intent信息作定位的,好比Content Provider,它的查找,都是依據Uri,有了HashMap,一切都瓜熟蒂落了。
ActivityManagerService用一些名曰xxxRecord的數據結構,來表達各個存活的組件。因而就有了,HistoryRecord(保存Activity信息的,之因此叫History,是相對Task棧而言的...),ServiceRecord,BroadcastRecord,ContentProviderRecord,TaskRecord,ProcessRecord,等等。
值得注意的,是TaskRecord,咱們一直再說,Task棧這樣的概念,其實,真實的底層,並不會在TaskRecord中,維繫一個Activity 的棧。在ActivityManagerService中,各個任務的Activity,都以HistoryRecord的形式,集中存放在一個 ArrayList中,每一個HistoryRecord,會存放它所在TaskRecord的引用。當有一個Activity,執行完成,從概念上的 Task棧中退出,Android是經過從當前HistoryRecord位置往前掃描同一個TaskRecord的HistoryRecord來完成的。這個設計,使得上層不少看上去邏輯很複雜的Task體系,在實現變得很統一而簡明,值得稱道。
ProcessRecord,是整個進程託管實現的核心,它存放有運行在這個進程上,全部組件的信息,根據這些信息,系統有一整套的算法來決議如何處置這個進程,若是對回收算法感興趣,能夠從ActivityManagerService的trimApplications函數入手來看。
對於開發者來講,去了解這部分實現,主要是能夠幫助理解整個進程和任務的概念,若是以爲這塊理解的清晰了,就不用去碰ActivityManagerService這個龐然大物了
第二中解釋:
應用程序的組件第一次運行時,Android將啓動一個只有一個執行線程的Linux進程。默認,應用程序全部的組件運行在這個進程和線程中。然而,你能夠安排組件運行在其餘進程中,且你能夠爲進程衍生出其它線程。本文從下面幾點來介紹Android的進程與線程:
1、進程
組件運行於哪一個進程中由清單文件控制。組件元素——、、、,都有一個process屬性能夠指定組件運行在哪一個進程中。這個屬性能夠設置爲每一個組件運行在本身的進程中,或者某些組件共享一個進程而其餘的不共享。他們還能夠設置爲不一樣應用程序的組件運行在同一個進程中——假設這些應用程序共享同一個Linux用戶ID且被分配了一樣的權限。元素也有process屬性,爲全部的組件設置一個默認值。
全部的組件都在特定進程的主線程中實例化,且系統調用組件是由主線程派遣。不會爲每一個實例建立單獨的線程,所以,對應這些調用的方法——諸如View.onKeyDown()報告用用戶的行爲和生命週期通知,老是運行在進程的主線程中。這意味着,沒有組件當被系統調用時應該執行很長時間或阻塞操做(如網絡操做或循環計算),由於這將阻塞進程中的其它組件。你能夠爲長操做衍生獨立的線程。
public boolean onKeyDown(int keyCode,KeyEvent event):默認實現KeyEvent.Callback.onKeyMultiple(),當按下視圖的KEYCODE_DPAD_CENTER或KEYCODE_ENTER而後釋放時執行,若是視圖可用且可點擊。
參數
keyCode-表示按鈕被按下的鍵碼,來自KeyEvent
event-定義了按鈕動做的KeyEvent對象
返回值
若是你處理事件,返回true;若是你想下一個接收者處理事件,返回false。
當內存剩餘較小且其它進程請求較大內存並須要當即分配,Android要回收某些進程,進程中的應用程序組件會被銷燬。當他們再次運行時,會從新開始一個進程。
當決定終結哪一個進程時,Android會權衡他們對用戶重要性的相對權值。例如,與運行在屏幕可見的活動進程相比(前臺進程),它更容易關閉一個進程,它的活動在屏幕是不可見(後臺進程)。決定是否終結進程,取決於運行在進程中的組件狀態。關於組件的狀態,將在後面一篇——組件生命週期中介紹。
2、線程
雖然你可能會將你的應用程序限制在一個進程中,但有時候你會須要衍生一個線程作一些後臺工做。由於用戶界面必須很快地響應用戶的操做,因此活動寄宿的線程不該該作一些耗時的操做如網絡下載。任何不可能在短期完成的操做應該分配到別的線程。
線程在代碼中是用標準的Java線程對象建立的,Android提供了一些方便的類來管理線程——Looper用於在線程中運行消息循環、Handler用戶處理消息、HandlerThread用戶設置一個消息循環的線程。
Looper類
該類用戶在線程中運行消息循環。線程默認沒有消息循環,能夠在線程中調用prepare()建立一個運行循環;而後調用loop()處理消息直到循環結束。大部分消息循環交互是經過Handler類。下面是一個典型的執行一個Looper線程的例子,分別使用prepare()和loop()建立一個初始的Handler與Looper交互:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
更多的關於Looper的信息及Handler、HandlerThread請參閱相關資料。
2.1、遠程過程調用(Remote procedure calls,RPCs)
Android有一個輕量級的遠程過程調用機制——方法在本地調用卻在遠程(另一個進程中)執行,結果返回給調用者。這須要將方法調用和它伴隨的數據分解爲操做系統可以理解的層次,從本地進程和地址空間傳輸到遠程進程和地址空間,並從新組裝調用。返回值以相反方向傳輸。Android提供了作這些工做的全部代碼,這樣咱們能夠專一於定義和執行RPC接口自己。
一個RPC接口僅包含方法。全部的方法同步地執行(本地方法阻塞直到遠程方法執行完成),即便是沒有返回值。簡言之,該機制工做原理以下:首先,你用簡單的IDL(interface definition language,接口定義語言)聲明一個你想實現的RPC接口。從這個聲明中,aidl工具生成一個Java接口定義,提供給本地和遠程進程。它包含兩個內部類,以下圖所示:
內部類有管理你用IDL定義的接口的遠程過程調用所須要的全部代碼。這兩個內部類都實現了IBinder接口。其中之一就是在本地由系統內部使用,你寫代碼能夠忽略它。另一個是Stub,擴展自Binder類。除了用於有效地IPC(interprocess communication)調用的內部代碼,內部類在RPC接口聲明中還包含方法聲明。你能夠定義Stub的子類實現這些方法,如圖中所示。
一般狀況下,遠程過程有一個服務管理(由於服務能通知系統關於進程和它鏈接的其它進程的信息)。它有由aidl工具生成的接口文件和Stub子類實現的RPC方法。服務的客戶端僅有由aidl工具生成的接口文件。
下面介紹服務如何與它的客戶端創建鏈接:
·服務的客戶端(在本地端的)應該實現onServiceConnected() 和onServiceDisconnected() 方法,所以當與遠程服務創建鏈接成功和斷開鏈接是會通知它。而後調用bindService() 創建鏈接。
·服務的onBind()方法將實現爲接受或拒絕鏈接,者取決於它接受到的意圖(該意圖傳送到binServive())。若是鏈接被接受,它返回一個Stub子類的實例。
·若是服務接受鏈接,Android調用客戶端的onServiceConnected()方法且傳遞給它一個IBinder對象,返回由服務管理的Stub子類的一個代理。經過代理,客戶端能夠調用遠程服務。
這裏只是簡單地描述,省略了一些RPC機制的細節。你能夠查閱相關資料或繼續關注Android開發之旅,後面將爲你奉上。
2.2、線程安全方法
在一些狀況下,你實現的方法可能會被不止一個線程調用,所以必須寫成線程安全的。這對遠程調用方法是正確的——如上一節討論的RPC機制。當從IBinder進程中調用一個IBinder對象中實現的一個方法,這個方法在調用者的線程中執行。然而,當從別的進程中調用,方法將在Android維護的IBinder進程中的線程池中選擇一個執行,它不在進程的主線程中執行。例如,一個服務的onBind()方法在服務進程的主線程中被調用,在onBind()返回的對象中執行的方法(例如,實現RPC方法的Stub子類)將在線程池中被調用。因爲服務能夠有一個以上的客戶端,因此同時能夠有一個以上的線程在執行同一個IBinder方法。所以,IBinder的方法必須是線程安全的。
一樣,一個內容提供者能夠接受其它進程產生的數據請求。雖然ContentResolver 和 ContentProvider 類隱藏進程通訊如何管理的,對應哪些請求的ContentResolver 方法——query()、insert()、delete()、update()、getType(),在內容提供者的進程的線程池中被調用,而不是在這一進程的主線程中。由於這些方法能夠同時從任意數量的線程中調用,他們也必須實現爲線程安全的。