6、 啟動方式
<activity>下的launchMode屬性可以設置四種啟動方式:
"standard" (the default mode)
"singleTop"
"singleTask"
"singleInstance"
這些不同的方式可以從下面的四點來說:
(1) 對一個Intent作出回應時哪個task應該去持有這個Activity。
對standard和singleTop方式來說,新的Activity和通過startActivity調用他的Activity處在同一個task中,如果調用時Intent對像裡含有FLAG_ACTIVITY_NEW_TASK標誌,那麼就像前面講的那樣的尋找一個新的task。
相反的,singTask和singleInstance方式,總是標誌Activity為task的root Activity,他們不會進入到其他的task中。
(2) 一個Activity是否可以有多個實例。
一個standard或者singleTop屬性的Activity可以實例化多次,他們可以屬於多個不同的task。
相反的,singleTask或者singleInstance屬性的Activity只能有一個實例(單例)。
(3) 實例是否能允許在task裡有其他的Activity。
一個singleInstance屬性的Activity單獨的在他自己的task裡,並且這個task裡只能有他自己一個Activity,如果他啟動了另一個Activity,那個Activity會根據啟動模式來啟動並裝進一個不同的task裡。其他的方面,singleInstance和singleTask一樣的。
其他三個方式允許有多個Activity在一個task裡,一個singleTask屬性的Activity總是一個task裡的root Activity,但是他可以啟動另外的Activity並且將這個新的Activity裝進同一個task裡,standard和singleTop屬性的Activity可以出現在task的任何地方。
(4) 一個類(Activity)的對象是否可以被啟動來處理一個新的Intent。
對默認的standard方式,會實例化一個對像來處理每一個新的Intent,每個實例處理一個新的Intent,對singleTop方式,如果一個已經存在的實例是在task的棧頂,那麼就重用這個實例來處理這個新的Intent,如果這個實例不在棧頂,那就不復用他,而是重新創建一個實例來處理這個新的Intent並且將這個實例壓入堆棧。
例如現在有一個task堆棧ABCD,A是root Activity,D是棧頂Activity,現在有一個啟動D的Intent來了,如果D是默認的standard方法,那麼就會創建一個新的實例來處理這個Intent,所以這個堆棧就變為ABCDD,然而如果D是singleTop方式,這個已經存在的棧頂的D就會來處理這個Intent,所以堆棧還是ABCD。D此時調用onNewIntent(),此時D可以調用getIntent()來獲得最初的Intent,或者調用setIntent()來更新這個Intent。
如果現在有一個Intent來啟動B,不管B是standard還是singleTop(因為現在B不在棧頂),都會創建一個新的實例,所以堆棧變為ABCDB
在一個task裡,對singleTask和singleInstance屬性的Activity只能有一個實例。所以這僅有的一個會來處理所以的Intent,一個singleInstance屬性Activity總在棧頂(因為task裡就只有他一個Activity),所以他會處理所以的Intent,但是一個singleTask屬性的Activity必須是task的root Activity(也就是必須在棧底),不能確定他的上面是否還有其他的Activity,如果沒有,就可以處理,如果還有其他的Activity,那麼如果現在有一個Intent來啟動這個singleTask屬性的Activity,這個Intent將會被丟掉(即使是這個Intent被丟掉,他的到來還是會導致這個task回到前台)。
當創建一個類(Activity)的實例來處理一個新的Intent時,用戶可以按下BACK鍵回到上一個Activity,但是如果是用已經存在的棧頂的Activity來處理Intent的話,按下BACK鍵是不能回到以前的狀態的(沒處理這個Intent之前)。
7、 清理堆棧
當用戶離開一個task一段時間後,系統就會清理掉task裡出了rootActivity以外的Activity,如果用戶又回來了,顯示的是rootActivity,就像是用戶離開又回來,是放棄以前的東西,開始新的東西。
上面說的是默認的情況,有一些Activity的屬性可以用來控制和修改這些行為。
alwaysRetainTaskState
如果一個task裡的root Activity的alwaysRetainTaskState屬性設置為true,那麼前面描述的默認情況就不會出現了,task即使過了一段時間也會一直保留所有的Activity。
clearTaskOnLaunch
如果一個task裡的root Activity的clearTaskOnLaunch屬性設置為true,和alwaysRetainTaskState相反,即使是一瞬間的離開,系統馬上就會清理掉task裡出rootActivity以外的所有Activity。
finishOnTaskLaunch
這個屬性和clearTaskOnLaunch一樣,但是他是對一個Activity起作用,不是整個task,他能引起所有的Activity離開,包括root Activity,當這個屬性設置為true,只是當用戶使用這個應用時Activity才在task裡,一旦用戶離開後重新回來,顯示的不是當前的界面。
還有其他的方法來從task裡強制移動Activity,如果一個Intent對像裡包含FLAG_ACTIVITY_CLEAR_TOP標誌,並且目標task裡已經一個在自己task裡可以處理Intent的Activity(就是處理這個Intent無需實例化另外一個Activity),那麼在這個Activity之上的所有Activity將被清除,能處理這個Intent的Activity就移到棧頂來處理這個Intent,例如ABCD堆棧,含有FLAG_ACTIVITY_CLEAR_TOP標誌的Intent來啟動B,那麼清除CD,B到達棧頂來響應Intent,此時是AB,如果B設置了standard屬性,那麼還是清楚CD,然後再創建一個實例來響應Intent,此時是ABB,因為standard屬性的Activity總是創建一個新的實例來響應新的Intent。
8、 進程和線程(Processes and Threads)
當一個應用的第一個組件需要運行時,android系統就為這個組件啟動一個只有一個線程的Linux進程,默認的,應用的所有組件都運行這個進程中的這個線程中。
但是,你可以安排組件運行在其他的進程中,並且為你的任意的進程增加若幹線程。
1、 進程
組件運行的進程是在manifest文件裡控制的,四大組件都一個process屬性可以指定進程來運行,這些屬性可以被設置為了每個組件都可以運行在他自己的進程中,或者幾個組件共享一個進程,或者不共享,如果兩個應用共享一個Linux user ID並且有相同的權限,那麼就可以使這兩個應用中的組件運行在相同的進程中,<application>也有process屬性,用來指定對所有組件的屬性。
所有的組件都在指定的進程中的主線程中實例化,系統調用這些組件就是從主線程裡發出的,其他的線程將不會對每個組件再實例化,所有作為調用的回應的這些方法,比如說View.onKeyDown()還是組件的生命週期函數等等都是運行在這個主線程中的,這就意味著當系統調用這個組件時,這個組件不能長時間的阻塞線程(比如說網絡操作,循環計算),因為這樣會阻塞這個進程中的其他組件,你可以將很耗時的任務分到其他的線程中。
當內存不足或者有其他更緊急的進程要求時,Android系統可能關閉一個進程,運行在這個進程中的應用組件因此被銷毀,當用戶又重新回來時,進程才被重新啟動。
至於究竟要停止哪個進程,android系統是通過衡量哪個進程對用戶來說更重要來實現的
2、 線程
你可以限制你的應用運行在一個進程中,但是有的時候你需要新開一個線程在後台運行,用戶界面需要隨時對用戶的要求做出反應,所以一些很耗時的工作應該重新啟動一個線程來做,以免阻塞主進程。
Android系統提供了一系列方便的類來管理線程(Looper,Handler,HandlerThread)
3、 遠程調用(Remote procedure calls)
Android系統有一個輕量級的遠程調用機制(RPC)-----一個方法在本地調用,但是在遠程執行(在另外一個進程裡),返回給調用端的所有結果都必須的系統能理解的,將數據從本地進程和地址空間傳遞到遠程的進程和地址空間,並在遠端重新裝配,返回值的時候傳輸方向相反,android系統會去做這些傳輸的工作,讓你能夠集中精力來定義你的RPC
一個RPC接口只能包含方法,默認的,即使是沒有值返回,所有的方法都是同步執行的,就是說本地方法一直會阻塞直到遠端的方法執行完畢)。
簡單的說,這個遠程調用的機制是這樣工作的:
首先你需要用IDL(interface definition language)聲明你的RPC接口,然後android系統會使用aidl工具來形成一個java接口,並且這個java接口是本地進程和遠端進程都可以獲得的,這個java接口包含了兩個內部類,請看下圖:
這兩個內部類有管理遠程調用(你用IDL聲明的接口)的所以代碼,兩個內部類都實現IBinder接口,一個是在本地(內部)使用,這個你可以不用自己寫代碼,另外一個叫做Stub,繼承自Binder類的,包含所有完成進程間通信(IPC)的代碼,他包含你在RPC接口中聲明的所有方法,你應該繼續繼承Stub類來實現這些方法。
一般的,遠端進程應該由一個service來管理(因為一個service能通知系統關於這個進程和他連接到的其他進程)。
9、 進程的生命週期(Processes and lifecycles)
Android系統總是盡最大的努力來維持一個應用的進程,但系統的內存不足時就可能需要關閉一些舊的進程了,但是決定關閉哪個進程呢,android系統把所以的進程放進一個重要性樹裡,最低重要性的進程將會被停止,系統有5種重要性等級,重要性從高到低如下:
(1)、前台進程。一個前台進程是當前執行用戶請求的進程,如果有如下的一種情形的那麼他就是前台進程:
a、這個進程裡運行著一個正在和用戶交互的Activity(這個Activity的onResume()方法被調用)。
b、這個進程裡有綁定到當前正在和用戶交互的Activity的一個service
c、這個進程裡有一個service對象,這個service對像執行了至少一個他生命週期的函數(onCreate(), onStart(), or onDestroy()).
d、這個進程裡有一個執行了onReceive()方法的broadcastreceiver對像
只有一定數量的前台進程在任何時間都存在,他們只有在最後的時刻被停止---系統的內存太少了而不能運行這些僅有的前台進程了),一般的,在那個時刻,手機會重新設置內存頁的狀態,所以停止一些前台的進程是為了保持對用戶操作的快速響應。
(2) 可見進程。一個可見進程一個沒有任何前台顯示的組件,但是仍然可以影響到用戶當前屏幕所看見的東西,如果有如下的一種情形那麼他就是可見進程。
a、 這個進程裡一個Activity,但是這個Activity當前不是在前台顯示,但是仍然對用戶是可見的(這個Activity的onPause()方法被調用),比如說一個Activity調用一個dialog,那麼這個dialog是當前顯示的組件,這個Activity不是在前台顯示,但是對用戶是可見的。
b、 這個進程裡有一個綁定到一個可見Activity(如上所述的Activity)的service
一個可見進程是極端重要的,只有在為了顯示所有前台進程時,即顯示前台進程都不夠時,才會停止可見進程。
(3)、服務進程。一個服務進程是一個通過startService()啟動的但是沒有在前兩個分類中的進程,雖然服務進程不是用戶直接能看見的,但是他也總是做一些用戶很關心的事(如在後台播放mp3,從網絡上下載東西),所以系統會一直保持服務進程運行,除非內存不足以運行服務進程,前台進程,可見進程。
(4)後台進程。一個後台進程是運行一個當前對用戶是不可見的Activity(這個Activity的onStop()被調用),這些進程對用戶體驗沒有什麼直接的影響,當內存不足以運行前台進程,可見進程,服務進程時,可以隨時停止後台進程,通常有很多的後台進程在運行,系統會把這些後台進程放進一個LRU中(最近使用隊列),最近使用的就最後停止。
(5) 空進程。一個空進程就是進程裡沒有任何活動的應用組件,維持這種進程的唯一原因就是作為一種緩存,當一個組件需要啟動時加快啟動的速度,系統為了平衡進程緩存和核心緩存會停止這些空的進程。
Android系統會取一個進程裡的所以組件的最高重要性來安排進程的重要性,比如說,一個進程裡有一個service和一個可見的Activity,那麼這個進程會被安排成一個可見進程,而不是服務進程。
另外,一個進程的重要性有可能會因為其他進程的依賴而升高,一個進程不能比他所服務的進程的重要性低,比如有進程A裡的service綁定到了進程B的組件上,那麼進程A的重要性至少和進程B的一樣,或者更高。
因為一個服務進程的重要性比運行一個後台Activity的進程高,所以,當一個Activity做一些長時間運行的任務時,最好啟動一個service來做,而不是放到一個線程裡去做,特別是這個任務的時間可能比Activity運行的時間還長的時候,比如在後台播放音樂,或者上傳一張圖片到網上,使用一個service保證了這個任務至少是服務進程的重要性,broadcast receiver也是一樣,長時間運行的任務也最好是放到一個service裡,而不是放到一個線程裡。 |