[coolxing按: 轉載請註明作者和出處, 如有謬誤, 歡迎在評論中指正.]
多線程在所有程式設計語言中都是比較難以理解和掌握的, 這幾天上網看了很多android多線程方面的資料, 在這裡做一些總結.
什麼時候使用多線程:
1. 耗時操作使用多線程, 耗時操作放在UI線程中會導致使用者的操作無法得到響應.
2. 阻塞操作使用多線程, 理由同上.
3. 多核CUP的裝置使用多線程, 可以有效提高CPU的利用率.
4. 並行操作使用多線程.
android中的多執行緒模式主要涉及的類有:Looper, Handler, MessageQueue, Message等.
Looper類用來建立訊息佇列. 每個線程最多隻能有一個訊息佇列, android中UI線程預設具有訊息佇列, 但非UI線程在預設情況下是不具備訊息佇列的. 如果需要在非UI線程中開啟訊息佇列, 需要調用Looper.prepare()方法, 在該方法的執行過程中會建立一個Looper對象, 而Looper的建構函式中會建立一個MessageQueue instance(Looper的建構函式是私人的, 在Looper類之外無法建立其對象). 此後再為該線程綁定一個Handler instance, 然後調用Looper.loop()方法, 就可以不斷的從訊息佇列中取出訊息和處理訊息了. Looper.myLoop()方法可以得到線程的Looper對象, 如果為null, 說明此時該線程尚未開啟訊息佇列.
Handler類用於處理訊息. 該類具有四個建構函式:
1. public Handler(). 建立好的Handler instance將綁定在代碼所在的線程的訊息佇列上, 因此一定要確定該線程開啟了訊息佇列, 否則程式將發生錯誤. 使用這個建構函式建立Handler instance, 一般來說, 我們需要重寫Hanler類的handleMessage()方法, 以便在之後的訊息處理時調用.
2. public Handler(Callback callback). Callback是Handler內部定義的一個介面, 因此想要使用這個建構函式建立Handler對象, 需要自訂一個類實現Callback介面, 並重寫介面中定義的handleMessage()方法. 這個建構函式其實與無參的建構函式類似, 也要確保代碼所在的線程開啟了訊息佇列. 不同的是在之後處理訊息時, 將調用callback的handleMessage()方法, 而不是Handler對象的handleMssage()方法.
3. public Handler(Looper looper). 這個建構函式表示建立一個Handler instance, 並將其綁定在looper所在的線程上. 此時looper不能為null. 此時一般也需要重寫Hanler類的handleMessage()方法
4. public Handler(Looper looper, Callback callback). 可以結合2和3理解.
MessageQueue類用於表示訊息佇列. 隊列中的每一個Message都有一個when欄位, 這個欄位用來決定Message應該何時出對處理. 訊息佇列中的每一個Message根據when欄位的大小由小到大排列, 排在最前面的訊息會首先得到處理, 因此可以說訊息佇列並不是一個嚴格的先進先出的隊列.
Message類用於表示訊息. Message對象可以通過arg1, arg2, obj欄位和setData()攜帶資料, 此外還具有很多欄位. when欄位決定Message應該何時出對處理, target欄位用來表示將由哪個Handler對象處理這個訊息, next欄位表示在訊息佇列中排在這個Message之後的下一個Message, callback欄位如果不為null表示這個Message封裝了一個runnable對象, what欄位表示code, 即這個訊息具體是什麼類型的訊息. 每個what都在其handler的namespace中, 我們只需要確保將由同一個handler處理的訊息的what屬性不重複就可以.
將訊息壓入訊息佇列: Message對象的target欄位關聯了哪個線程的訊息佇列, 這個訊息就會被壓入哪個線程的訊息佇列中.
1. 調用Handler類中以send開頭的方法可以將Message對象壓入訊息佇列中, 調用Handler類中以post開頭的方法可以將一個runnable對象封裝在一個Message對象中, 然後再壓入訊息佇列, 此時入隊的Message其callback欄位不為null, 值就是這個runnable對象. 調用Handler對象的這些方法入隊的Message, 其target屬性會被賦值為這個handler對象.
2. 調用Message對象的sendToTarget()方法可以將其本身壓入與其target欄位(即handler對象)所關聯的訊息佇列中.
將未來得及處理的訊息從訊息佇列中刪除:
調用Handler對象中以remove開頭的方法就可以.
從訊息佇列中取出訊息並處理訊息: 所有在訊息佇列中的訊息, 都具有target欄位. 訊息是在target所關聯的線程上被取出和處理的.
1. 如果取出的Message對象的callback欄位不為null, 那麼就調用callback欄位的run()方法(callback欄位的類型是runnable). 注意此時並不開啟一個新的線程運行run()方法, 而是直接在handler對象(即Message的target欄位)所關聯的線程上運行.
2. 如果取出的Message對象的callback欄位為null, 且Handler對象中的callback欄位也為null, 那麼這個訊息將由Handler對象的handleMessage(msg)方法處理. 注意Message對象的callback欄位是Runnable類型的而Handler對象的callback欄位是Callback類型的, Handler對象的callback欄位是在建立Handler instance的時候指定的, 如果沒有指定則這個欄位為null, 詳見Handler類的四個構造方法.
3. 如果取出的Message對象的callback欄位為null, 且Handler對象中的callback欄位不為null, 那麼這個訊息將由Handler對象中的callback欄位的handleMessage方法處理.
線程間通訊: 有了以上的敘述, 線程間的通訊也就好理解了. 假如一個handler關聯了A線程上的訊息佇列, 那麼我們可以在B線程上調用handler的相關方法向A線程上的訊息佇列壓入一個Message, 這個Message將在A線程上得到處理.
android中還有一些和多線程有關的類, 比如AsyncTask, HandlerThread等, 這些以後再總結.
摘自 coolxing