自我理解: 鏈接
1. 首先有一個(gè)Looper對(duì)象已經(jīng)存在,并處于輪詢(xún)的狀態(tài);
相對(duì)應(yīng)的代碼卿樱,Looper.prepare()-->Looper.loop()
2. Handler發(fā)送Message,加入到MQ中
各種發(fā)送消息的方法-->equeueMessage()-->MessageQueue.equeueMessage()
3. 輪詢(xún)?nèi)〕鯩Q的隊(duì)頭Message空盼,通過(guò)Handler進(jìn)行回調(diào)
MessageQueue.next()-->(handler)msg.target.dispatchMessage()-->handleMessage()[自定義TODO]
Message 【繼承Parcelable】
封裝了任務(wù)攜帶的參數(shù)和處理該任務(wù)的Handler
/**
* User-defined message code so that the recipient can identify
* what this message is about. Each {@link Handler} has its own name-space
* for message codes, so you do not need to worry about yours conflicting
* with other handlers.
*/
public int what; //標(biāo)識(shí)位肢扯,用來(lái)區(qū)別消息
/**
* arg1 and arg2 are lower-cost alternatives to using
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
public int arg1; //用來(lái)存儲(chǔ)一些整數(shù)值芦倒,替代setData()存儲(chǔ)
/**
* arg1 and arg2 are lower-cost alternatives to using
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
public int arg2; //用來(lái)存儲(chǔ)一些整數(shù)值轰豆,替代setData()存儲(chǔ)
/**
* An arbitrary object to send to the recipient. When using
* {@link Messenger} to send the message across processes this can only
* be non-null if it contains a Parcelable of a framework class (not one
* implemented by the application). For other data transfer use
* {@link #setData}.
*
* <p>Note that Parcelable objects here are not supported prior to
* the {@link android.os.Build.VERSION_CODES#FROYO} release.
*/
//發(fā)送給收件人的任意對(duì)象雪营。當(dāng)使用{@link Messenger}跨進(jìn)程發(fā)送消息時(shí)弓千,
//如果它包含框架類(lèi)的Parcelable(不是應(yīng)用程序?qū)崿F(xiàn)的框架類(lèi)),則它只能是非null献起。
//對(duì)于其他數(shù)據(jù)傳輸洋访,請(qǐng)使用{@link #setData}
//2.2版本之前不支持實(shí)現(xiàn)Parceable的實(shí)體類(lèi)
public Object obj;
-
Message的創(chuàng)建(2種方法)
- 直接實(shí)例化一個(gè)Message镣陕,然后設(shè)置其參數(shù)
Message msg = new Message; msg.what = 0; msg.arg0 = 1; ...
- Message.obtain() --
【推薦】
在許多情況下可以避免分配新的對(duì)象;避免重復(fù)創(chuàng)建Message
- 直接實(shí)例化一個(gè)Message镣陕,然后設(shè)置其參數(shù)
-
Message注意點(diǎn):
- 盡量通過(guò)Message.obtain()的方式構(gòu)建Message對(duì)象姻政,防止Message的多次創(chuàng)建呆抑;
- 僅有int型參數(shù)時(shí) 最好使用arg1和arg2,減少內(nèi)存的使用汁展;
Looper:消息通道(使普通線(xiàn)程變成Looper線(xiàn)程)
在Activity中鹊碍,系統(tǒng)會(huì)自動(dòng)啟動(dòng)Looper對(duì)象;而在自定義類(lèi)中食绿,需要自己手動(dòng)調(diào)用侈咕;
下面是Looper線(xiàn)程實(shí)現(xiàn)的典型示例(源碼中的注釋中有寫(xiě)到):
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
關(guān)于Looper.prepare()系統(tǒng)源碼:
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
"http://將當(dāng)前線(xiàn)程初始化為looper;確保在調(diào)用prepare方法之后炫欺,調(diào)用loop方法乎完;然后結(jié)束時(shí)調(diào)用quit方法;"
public static void prepare() {
prepare(true);
}
'//一個(gè)Thread只能有一個(gè)Looper對(duì)象'
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
//初始化時(shí)會(huì)創(chuàng)建一個(gè)MessageQueue對(duì)象和線(xiàn)程
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
關(guān)于Looper.loop()系統(tǒng)源碼:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
//在此線(xiàn)程中運(yùn)行消息隊(duì)列,確保在結(jié)束loop后調(diào)用quit
public static void loop() {
//獲取當(dāng)前l(fā)ooper線(xiàn)程
final Looper me = myLooper();
//判斷是否調(diào)用了prepare
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//獲取線(xiàn)程中的消息隊(duì)列
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
...
'//for(;;)和while(true) for (品洛;树姨;)指令少,不占用寄存器桥状,而且沒(méi)有判斷跳轉(zhuǎn)'
for (;;) {
// 獲取消息隊(duì)列中的message
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
... log日志[省略]...
try {
'//交給Message對(duì)應(yīng)的Handler處理消息'
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
... [省略]...
msg.recycleUnchecked();
}
}
總結(jié):
1)一個(gè)Thread只能有一個(gè)Looper對(duì)象帽揪;
2)Looper內(nèi)部有一個(gè)消息隊(duì)列,loop()方法調(diào)用后線(xiàn)程開(kāi)始不斷從隊(duì)列中取出消息執(zhí)行辅斟;
3)Looper使一個(gè)線(xiàn)程變成Looper線(xiàn)程转晰。
MessageQueue:消息隊(duì)列
根據(jù)源碼中的注釋翻譯如下:
保存由{@link Looper}分派的消息列表的低級(jí)類(lèi)。消息不會(huì)直接添加到MessageQueue士飒,而是通過(guò)與Looper關(guān)聯(lián)的{@link Handler}對(duì)象添加查邢。
您可以使用{@link Looper#myQueue()Looper.myQueue()}檢索當(dāng)前線(xiàn)程的MessageQueue。
/**
Low-level class holding the list of messages to be dispatched by a
{@link Looper}. Messages are not added directly to a MessageQueue,
but rather through {@link Handler} objects associated with the Looper.
<p>You can retrieve the MessageQueue for the current thread with
{@link Looper#myQueue() Looper.myQueue()}.
*/
public final class MessageQueue {
...
//獲取下一個(gè)message方法
Message next(){
...
}
//message添加到消息隊(duì)列的方法
boolean enqueueMessage(Message msg, long when) {
...
}
}
Handler:消息操作類(lèi)【重點(diǎn)】
官方注釋?zhuān)ǔ橄蟮姆g):
Handler允許您發(fā)送和處理與線(xiàn)程{@link MessageQueue}關(guān)聯(lián)的{@link Message}和Runnable對(duì)象酵幕。
每個(gè)Handler實(shí)例都與一個(gè)線(xiàn)程和該線(xiàn)程的消息隊(duì)列相關(guān)聯(lián)扰藕。
當(dāng)您創(chuàng)建一個(gè)新的Handler時(shí),它被綁定到正在創(chuàng)建它的線(xiàn)程的線(xiàn)程/消息隊(duì)列 - 從那時(shí)起芳撒,它將消息和runnables傳遞給該消息隊(duì)列并在消息出來(lái)時(shí)執(zhí)行它們隊(duì)列邓深。
Handler有兩個(gè)主要用途:
(1)將消息和runnable安排在將來(lái)的某個(gè)點(diǎn)上執(zhí)行;
(2)將要在不同于自己的線(xiàn)程上執(zhí)行的動(dòng)作排入隊(duì)列。
源碼分析:
/**
*默認(rèn)的構(gòu)造方法會(huì) 關(guān)聯(lián)一個(gè)looper
*/
public Handler(Callback callback, boolean async) {
...
//關(guān)聯(lián)looper笔刹,不能為null
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//直接獲取looper的消息隊(duì)列芥备,
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
handler發(fā)送消息:
可傳遞參數(shù)包括Message和Runnable,但即使傳遞Runnable對(duì)象舌菜,最終也被處理為Message對(duì)象萌壳,然后執(zhí)行sendMessageAtTime()方法
/**
* 在所有待處理消息之后將消息排入消息隊(duì)列。
* 如果消息成功的加入到MQ中,就返回true
*/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
handler消息的處理:
dispatchMessage(msg);此方法在Looper.loop()中調(diào)用msg.target.dispatchMessage()讶凉;這里的msg.traget就是指向當(dāng)前的handler染乌;
然后Handler中調(diào)用handleMessage(msg)方法山孔,來(lái)觸發(fā)我們需要實(shí)現(xiàn)的具體邏輯懂讯。
延伸:
1. Handler內(nèi)部如何獲取到當(dāng)前線(xiàn)程的Looper?
答:是通過(guò) ThreadLocal
2. 系統(tǒng)為什么不允許子線(xiàn)程中訪(fǎng)問(wèn)UI台颠?
答:Android中的UI控件不是線(xiàn)程安全的褐望,如果在多線(xiàn)程中并發(fā)的訪(fǎng)問(wèn),UI顯示狀態(tài)不可預(yù)計(jì)串前。
3. 為何系統(tǒng)不對(duì)UI的訪(fǎng)問(wèn)加上鎖機(jī)制瘫里?
答:上鎖會(huì)讓UI訪(fǎng)問(wèn)的邏輯變得復(fù)雜,會(huì)降低UI訪(fǎng)問(wèn)的效率荡碾,也會(huì)阻塞某些線(xiàn)程的執(zhí)行谨读,體現(xiàn)在界面上會(huì)顯得卡頓運(yùn)行不暢。