當應用程式啟動時,系統會為其建立一個進程,也會建立一個線程名字叫做main,所有其所屬組件的建立,系統事件的處理,系統的回調等一切應用相關的事情都運行在此名叫main的線程中。此線程即為常說的主線程(main thread)。俗稱的UI線程(UI thread)也是它,因為只有主線程可以操作UI相關的事情,所以有人把主線程也稱作UI線程。為什麼非主線程不能操作UI呢?因為對UI操作常常會引發系統的回調,所以如果允許第三線程來操作可能會引發系統回調的紊亂,進而會打亂整個架構的時序!
這裡要特別注意的就是同一個進程中的所有組件運行在同一個線程中,Activiy,Service,BoradcastReceiver和ContentProvider都運行在主線程中。最容易引起誤解的就是Service,文檔和常識都會認為Service是放在後台用於操作費時運算的,但是實則不然,如果你在Service中做費時操作,同樣會引發臭名昭著的ANR(Application Not Responding)。所以如果想把Service當做一個Server,必須在Service用HandlerThread或Thread建立一個Worker線程!
Activity也是一樣的,你startActivity()後,開啟了一個新的Activity,但它們都運行在同一個線程中,所以你還是不能在原Activity中做費時操作!也即在調用startActivity()開啟了一個新的Activity後,或者在onPause(), onStop(), onDestroy()中做費時操作會引發ANR。
對於ContentProvider也是一樣的,如果跟其他組件在同一進程內,那麼調用ContentResolver的方法會相當於直接調用ContentProvider的方法。如果是在另外一個進程中,雖是通過IPC,但也是同步的,因為IBinder的同步的,也即調用ContentResolver時會把調用者的進程掛起,等待ContentProvider的進程操作結束,再把結果傳給調用者進程!所以,如果ContentProvider中有費時操作,或者會同步鎖資料庫等,也一定要注意ANR的發生!
所以一定要記住:一個進程只有一個主線程,所有組件都運行在主線程中。
因此,如果有費時操作,必須要建立Worker線程!
1. Message Message訊息,理解為線程間交流的資訊,處理資料後台線程需要更新UI,則發送Message內含一些資料給UI線程。
2. Handler Handler處理者,是Message的主要處理者,負責Message的發送,Message內容的執行處理。後台線程就是通過傳進來的Handler對象引用來sendMessage(Message)。而使用Handler,需要implement 該類的 handleMessage(Message)方法,它是處理這些Message的操作內容,例如Update UI。通常需要子類化Handler來實現handleMessage方法。
3. Message Queue Message Queue訊息佇列,用來存放通過Handler發布的訊息,按照先進先出執行。 每個message queue都會有一個對應的Handler。Handler會向message queue通過兩種方法發送訊息:sendMessage或post。這兩種訊息都會插在message queue隊尾並按先進先出執行。但通過這兩種方法發送的訊息執行的方式略有不同:通過sendMessage發送的是一個message對象,會被Handler的handleMessage()函數處理;而通過post方法發送的是一個runnable對象,則會自己執行。
4. Looper Looper是每條線程裡的Message Queue的管家。Android沒有Global的Message Queue,而Android會自動替主線程(UI線程)建立Message Queue,但在子線程裡並沒有建立Message Queue。所以調用Looper.getMainLooper()得到的主線程的Looper不為NULL,但調用Looper.myLooper()得到當前線程的Looper就有可能為NULL。
對於子線程使用Looper,API Doc提供了正確的使用方法:
- class LooperThread extends Thread {
- public Handler mHandler;
-
- public void run() {
- Looper.prepare(); //建立本線程的Looper並建立一個MessageQueue
-
- mHandler = new Handler() {
- public void handleMessage(Message msg) {
- // process incoming messages here
- }
- };
-
- Looper.loop(); //開始運行Looper,監聽Message Queue
- }
- }
這個Message機制的大概流程:
1. 在Looper.loop()方法運行開始後,迴圈地按照接收順序取出Message Queue裡面的非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對象。
Android另外提供了一個工具類:AsyncTask。它使得UI thread的使用變得異常簡單。它使建立需要與使用者介面互動的長時間啟動並執行任務變得更簡單,不需要藉助線程和Handler即可實現。
1) 子類化AsyncTask 2) 實現AsyncTask中定義的下面一個或幾個方法 onPreExecute() 開始執行前的準備工作; doInBackground(Params...) 開始執行幕後處理,可以調用publishProgress方法來更新即時的任務進度; onProgressUpdate(Progress...) 在publishProgress方法被調用後,UI thread將調用這個方法從而在介面上展示任務的進展情況,例如通過一個進度條進行展示。 onPostExecute(Result) 執行完成後的操作,傳送結果給UI 線程。 這4個方法都不能手動調用。而且除了doInBackground(Params...)方法,其餘3個方法都是被UI線程所調用的,所以要求: 1) AsyncTask的執行個體必須在UI thread中建立; 2) AsyncTask.execute方法必須在UI thread中調用; 同時要注意:該task只能被執行一次,否則多次調用時將會出現異常。而且是不能手動停止的,這一點要注意,看是否符合你的需求! 在使用過程中,發現AsyncTask的建構函式的參數設定需要看明白:
AsyncTask<Params, Progress, Result> Params對應doInBackground(Params...)的參數類型。而new AsyncTask().execute(Params... params),就是傳進來的Params資料,你可以execute(data)來傳送一個資料,或者execute(data1, data2, data3)這樣多個資料。 Progress對應onProgressUpdate(Progress...)的參數類型; Result對應onPostExecute(Result)的參數類型。 當以上的參數類型都不需要指明某個時,則使用Void,注意不是void。不明白的可以參考上面的例子,或者API Doc裡面的例子。