1. 程式人生 > >Android基礎和執行機制

Android基礎和執行機制

4、Activities and Tasks
  一個Activity可以啟動另一個Activity,即使這個Activity是定義在另一個應用裡的,比如說,你想展示給使用者一條街的地圖,現在已經有一個Activity可以做這件事,那麼現在你需要做的就是將你請求的資訊放進一個Intent物件裡,並且通過startActivity()傳遞給他,這個地圖就可以顯示出來了,但使用者按下BACK鍵時,你的Activity又重新出現在螢幕上。
  對使用者來說,顯示地圖的Activity和你的Activity好像在一個應用中的,即使是他們是定義在不用的應用中的,執行在各自的應用程序中,android將兩個Activity放進一個task裡,一個task是一組彼此聯絡的Activity,被安排在一個堆疊中,堆疊中的根Activity就是開闢這個task的,一般的,他是使用者選擇應用後首先啟動的那個Activity,堆疊頂部的Activity是當前正在執行的Activity,當一個Activity啟動另一個Activity時,新的Activity被壓進堆疊中,成為執行的Activity,當用戶按下BACK鍵,當前的Activity彈出堆疊,先前的Activity恢復成為執行的Activity。
  一個task就是一組Activity的堆疊,不是在manifest檔案裡的一個類,一個元素,所以沒有方法來為一個task裡的Activity獨立的設定值,對task設定值是在root Activity裡設定的。
  一個task裡的所有Activity組成一個單元,整個task(整個Activity堆疊)可以在前臺,也可以在後臺(應用程式的切換就是task的前後臺的切換),假設,當前的task有四個Activity在堆疊裡,使用者按下HOME鍵,去開啟另一個應用(實際上是一個新的task),那麼當前的task就退到後臺執行,新開啟的應用的root Activity此時就顯示出來了,然後,過了一段時間,使用者回到主介面,又重新選擇了以前的那個應用(先前的那個task),那麼先前的那個task此時又回到了前臺了,當用戶按下BACK鍵時,螢幕不是顯示剛剛關閉的那個應用,而是移除回到前臺的這個task堆疊棧頂Activity,將下一個Activity顯示出來。
  剛才描述的情況是Activity和task預設的行為,但是有很多的方法來對幾乎所有的方面進行修改,如Activity和task的聯絡。task裡Activity的行為,是受啟動它的Intent物件的flag和在manifest檔案中的Activity的屬性集合共同影響的。
  Flag:
  FLAG_ACTIVITY_NEW_TASK
  FLAG_ACTIVITY_CLEAR_TOP
  FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
  FLAG_ACTIVITY_SINGLE_TOP
  屬性:
  taskAffinity
  launchMode
  allowTaskReparenting
  clearTaskOnLaunch
  alwaysRetainTaskState
  finishOnTaskLaunch
5、Affinities and new tasks
  預設的,一個應用裡的所有Activity都有聯絡,所有都是屬於一個task的,然而,可以通過下的taskAffinity屬性來為每個Activity單獨的設定屬性關係,定義在不同應用中的Activity可以共享一種關係(affinity),或者定義在同一個應用中的Activity可以分配不同的關係(affinity)。這種關係在兩種情況下生效,當啟動Activity的 Intent物件包含有FLAG_ACTIVITY_NEW_TASK標誌,一個Activity的allowTaskReparenting屬性設定為true。
  FLAG_ACTIVITY_NEW_TASK
  一個Activity呼叫startActivity()啟動一個新的Activity時,新的Activity會壓入到相同的task中的,如果傳遞給startactivity()的Intent物件含有FLAG_ACTIVITY_NEW_TASK標誌,系統就會尋找一個新的task來裝這個新的Activity,然而,也不總是這麼做,如果已經有一個task和這個新的的Activity有相同的關係,那麼就把這個新的Activity放進這個task裡,如果沒有,就啟動一個新的task。
  allowTaskReparenting屬性
  如果一個Activity的allowTaskReparenting屬性設定為true,這個Activity就可以從啟動時的那個task移動到一個和他有關係的當前在前臺的一個task裡,比如,假設現在有一個天氣預報的Activity被定義在一個旅行的應用裡,他和這個應用裡的其他Activity有相同的關係(預設的關係),並且他允許reparenting,現在你自己應用有一個Activity啟動這個天氣預報的Activity,那麼天氣預報Activity就會移動到你的Activity所在的task裡,當旅行的應用又回到前臺時,天氣預報Activity重新回到以前的那個task並顯示。(個人觀點:如果說沒有設定這個屬性,或者這個屬性設定為false,那麼一個應用裡的Activity呼叫另一個應用裡的Activity時,系統是為另一個應用裡的Activity建立一個例項,然後放到同一個task裡,但是如果設定了allowTaskReparenting為true,那麼另一個應用裡的Activity是可以在不同的task間來回移動的,那個task在前臺就移動到那個task裡)
6、啟動方式
  下的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並且有相同的許可權,那麼就可以使這兩個應用中的元件執行在相同的程序中,也有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裡,而不是放到一個執行緒裡。