Android進程 與 消息模型
Android進程
同 時,Android會爲每個應用程序分配一個單獨的LINUX用戶。Android會盡量保留一個正在運行進程,只在內存資源出現不足時,Android 會嘗試停止一些進程從而釋放足夠的資源給其他新的進程使用,也能保證用戶正在訪問的當前進程有足夠的資源去及時地響應用戶的事件。Android會根據進程中運行的組件類別以及組件的狀態來判斷該進程的重要性,Android會首先停止那些不重要的進程。按照重要性從高到低一共有五個級別:
前臺進程
可見進程
服務進程
後臺進程
空進程
Android 對進程的重要性評級的時候,選取它最高的級別。另外,當被另外的一個進程依賴的時候,某個進程的級別可能會增高。一個爲其他進程服務的進程永遠不會比被服務的進程重要級低。因爲服務進程比後臺activity進程重要級高,因此一個要進行耗時工作的activity最好啓動一個service來做這個工 作,而不是開啓一個子進程――特別是這個操作需要的時間比activity存在的時間還要長的時候。例如,在後臺播放音樂,向網上上傳攝像頭拍到的圖片,使用service可以使進程最少獲取到“服務進程”級別的重要級,而不用考慮activity目前是什麼狀態。broadcast receivers做費時的工作的時候,也應該啓用一個服務而不是開一個線程。
當一個程序第一次啓動時,Android會同時啓動一個對應的主線程(Main Thread),主線程主要負責處理與UI相關的事件,如用戶的按鍵事件,用戶接觸屏幕的事件以及屏幕繪圖事件,並把相關的事件分發到對應的組件進行處理。所以主線程通常又被叫做UI線程。在開發Android應用時必須遵守單線程模型的原則: Android UI操作並不是線程安全的並且這些操作必須在UI線程中執行。
Android的UI是單線程(Single-threaded)的。爲了避免拖住GUI,一些較費時的對象應該交給獨立的線程去執行。如果幕後的線程來執行UI對象,Android就會發出錯誤訊息
CalledFromWrongThreadException。以後遇到這樣的異常拋出時就要知道怎麼回事了!
Message Queue:
在單線程模型下,爲了解決類似的問題,Android設計了一個Message Queue(消息隊列), 線程間可以通過該Message Queue並結合Handler和Looper組件進行信息交換。下面將對它們進行分別介紹:
1. Message
Message消息,理解爲線程間交流的信息,處理數據後臺線程需要更新UI,則發送Message內含一些數據給UI線程。
2. Handler
Handler處理者,是Message的主要處理者,負責Message的發送,Message內容的執行處理。後臺線程就是通過傳進來的 Handler對象引用來sendMessage(Message)。而使用Handler,需要implement
該類的 handleMessage(Message)
方法,它是處理這些Message的操作內容,例如UpdateUI。通常需要子類化Handler來實現handleMessage方法。
3. Message Queue
Message Queue消息隊列,用來存放通過Handler發佈的消息,按照先進先出執行。
每個message queue都會有一個對應的Handler。Handler會向messagequeue通過兩種方法發送消息:sendMessage或post。這兩種消息都會插在message queue隊尾並按先進先出執行。但通過這兩種方法發送的消息執行的方式略有不同:通過sendMessage發送的是一個message對象,會被
Handler的handleMessage()函數處理;而通過post方法發送的是一個runnable對象,則會自己執行。
4. Looper
Looper是每條線程裏的MessageQueue的管家。Android沒有Global的Message Queue,而Android會自動替主線程(UI線程)建立MessageQueue,但在子線程裏並沒有建立Message
Queue。所以調用Looper.getMainLooper()得到的主線程的Looper不爲NULL,但調用Looper.myLooper() 得到當前線程的Looper就有可能爲NULL。
對於子線程使用Looper,API Doc提供了正確的使用方法:
這個Message機制的大概流程:
1.
在Looper.loop()方法運行開始後,循環地按照接收順序取出MessageQueue裏面的非NULL的Message。
2. 一開始Message Queue裏面的Message都是NULL的。當Handler.sendMessage(Message)到Message Queue,該函數裏面設置了那個Message對象的target屬性是當前的Handler對象。隨後Looper取出了那個Message,則調用
該Message的target指向的Hander的dispatchMessage函數對Message進行處理。
在dispatchMessage方法裏,如何處理Message則由用戶指定,三個判斷,優先級從高到低:
1) Message裏面的Callback,一個實現了Runnable接口的對象,其中run函數做處理工作;
2) Handler裏面的mCallback指向的一個實現了Callback接口的對象,由其handleMessage進行處理;
3) 處理消息Handler對象對應的類繼承並實現了其中handleMessage函數,通過這個實現的handleMessage函數處理消息。
由此可見,我們實現的handleMessage方法是優先級最低的!
3. Handler處理完該Message (update UI)後,Looper則設置該Message爲NULL,以便回收!
在網上有很多文章講述主線程和其他子線程如何交互,傳送信息,最終誰來執行處理信息之類的,個人理解是最簡單的方法——判斷Handler對象裏面的Looper對象是屬於哪條線程的,則由該線程來執行!
1. 當Handler對象的構造函數的參數爲空,則爲當前所在線程的Looper;
2. Looper.getMainLooper()得到的是主線程的Looper對象,Looper.myLooper()得到的是當前線程的Looper對象。
Android1.5的工具類:AsyncTask:
使創建需要與用戶界面交互的長時間運行的任務變得更簡單。
相對來說AsyncTask更輕量級一些,適用於簡單的異步處理,不需要藉助線程和Handler即可實現。
AsyncTask是抽象類.AsyncTask定義了三種泛型類型Params,Progress和Result。
Params啓動任務執行的輸入參數,比如HTTP請求的URL。
Progress後臺任務執行的百分比。
Result後臺執行任務最終返回的結果,比如String。
AsyncTask的執行分爲四個步驟,每一步都對應一個回調方法,這些方法不應該由應用程序調用,開發者需要做的就是實現這些方法。
1)子類化AsyncTask
2)實現AsyncTask中定義的下面一個或幾個方法
onPreExecute(), 該方法將在執行實際的後臺操作前被UI thread調用。可以在該方法中做一些準備工作,如在界面上顯示一個進度條。
doInBackground(Params...), 將在onPreExecute方法執行後馬上執行,該方法運行在後臺線程中。這裏將主要負責執行那些很耗時的後臺計算工作。可以調用 publishProgress方法來更新實時的任務進度。該方法是抽象方法,子類必須實現。
onProgressUpdate(Progress...),在publishProgress方法被調用後,UI thread將調用這個方法從而在界面上展示任務的進展情況,例如通過一個進度條進行展示。
onPostExecute(Result), 在doInBackground執行完成後,onPostExecute 方法將被UI thread調用,後臺的計算結果將通過該方法傳遞到UI thread.
注意:
- 1)Task的實例必須在UI thread中創建
- 2)execute方法必須在UI thread中調用
- 3)不要手動的調用onPreExecute(), onPostExecute(Result),doInBackground(Params...),onProgressUpdate(Progress...)這幾個方法
- 4)該task只能被執行一次,否則多次調用時將會出現異常。doInBackground方法和onPostExecute的參數必須對應,這兩個參數在AsyncTask聲明的泛型參數列表中指定,第一個爲doInBackground接受的參數,第二個爲顯示進度的參數,第三個爲doInBackground返回和onPostExecute傳入的參數。