handler到底是如何完成執行緒切換的?這個問題要從handler最初的用法和原理講起。
首先我們列出正常情況下乙個handler使用的步驟然後講解分析如何實現,這裡不對一些基礎的概念做解釋,具體的請查閱原始碼。
handler的使用步驟:
1.呼叫looper.prepare();
2.建立handler物件;
3.呼叫looper.loop()方法。
4.執行緒中傳送訊息。
這樣我們就建立好了乙個handler可以採用sendmessage等方法傳送訊息。
那麼這幾步都幹了什麼?
第一步:呼叫prepare方法
private static void prepare(boolean quitallowed)
sthreadlocal.set(new looper(quitallowed));
}
建立了乙個looper物件儲存在了乙個threadlocal物件中,至於threadlocal是幹什麼的,查詢有關threadlocal如何儲存執行緒中資料的資料。
上面**中建立了乙個loper物件,new looper的時候我們可以看到:
private looper(boolean quitallowed)
// 建立了乙個messagequeue物件,這個就是乙個訊息佇列用來儲存我們傳送的message,其實這個messagequueue是乙個單鏈表。
後邊我們會知道這裡建立了乙個messagequeue物件,從字面上來看就是乙個訊息佇列,其實他是乙個鍊錶,鍊錶的資料結構就是便於插入和刪除。
第二步建立handler
public handler(callback callback, boolean async)
從構造方法中我們知道了hanlder中獲取了乙個looper物件,這個looper中儲存了乙個mqueue.這個lopper物件就是我們前面儲存在threadlocal中的那個。
第三步呼叫loop()
public static void loop()
final messagequeue queue = me.mqueue;
binder.clearcallingidentity();
final long ident = binder.clearcallingidentity();
for (;;)
......
final long start = (slowdispatchthresholdms == 0) ? 0 : systemclock.uptimemillis();
final long end;
try
我們清楚的看到:loop()方法中獲取了looper物件,取出了訊息佇列mqueue,然後開始迴圈。同時呼叫了messagequeue的next()方法取出乙個訊息最後呼叫了這個訊息的dispathchmessage();看完原始碼我們知道這個msg.tagget.dispatchmessage()就是handler中的訊息處理的方法。
public void dispatchmessage(message msg) else
}handlemessage(msg);
}}
這個方法的**中我們看到,首先判斷了callback是不是null.這是因為handler使用有兩種方法,一種可以通過建立callback的方式。最後呼叫了hanlder的handlemessage(msg);這個方法就是我們經常自己實現的訊息處理方法。所以我們就到達了目標。
第四步:傳送訊息
呼叫sendmessage()等方法傳送訊息,追蹤傳送訊息的方法,最後呼叫了以下的方法:
public boolean sendmessageattime(message msg, long uptimemillis)
return enqueuemessage(queue, msg, uptimemillis);
}
這裡拿到了我們前面handler構造方法儲存的mqueue,然後呼叫了enqueuemessage()方法。
private boolean enqueuemessage(messagequeue queue, message msg, long uptimemillis)
return queue.enqueuemessage(msg, uptimemillis);
}
最後看queue.enqueuemessage(msg, uptimemillis);在queue中處理了訊息。
boolean enqueuemessage(message msg, long when)
......
msg.markinuse();
msg.when = when;
message p = mmessages;
boolean needwake;
if (p == null || when == 0 || when < p.when) else
if (needwake && p.isasynchronous())
}msg.next = p; // invariant: p == prev.next
prev.next = msg;}}
return true;
}
也就是通過訊息要執行的時間,最後把訊息插入鍊錶中。
這樣我們就完成了訊息的傳送!
此刻:主線程的loop()方法一直在迴圈處理著訊息,我們傳送的訊息就會到loop()方法中去,最後交給handler處理。
那麼到底是怎麼樣就完成了執行緒的切換呢?
其實就是這個訊息傳送的過程,我們在不同的執行緒傳送訊息,執行緒之間的資源是共享的。也就是任何變數在任何執行緒都可以修改,只要做併發操作就好了。上述**中插入佇列就是加鎖的synchronized,handler中我們使用的是同乙個messagequeue物件,同一時間只能乙個執行緒對訊息進行入隊操作。訊息儲存到佇列中後,主線程的looper還在一直迴圈loop()處理。這樣主線程就能拿到子執行緒儲存的message物件,在我們沒有看見的時候完成了執行緒的切換。
所以總結來講就是:
1.建立了乙個looper物件儲存在threadlocal中。這個looper同時持有乙個messagequeue物件。
2.建立handler獲取到looper物件和messagequeue物件。在呼叫sendmessage方法的時候在不同的執行緒(子執行緒)中把訊息插入messagequeue佇列。
3.在主線程中(ui執行緒),呼叫looper的loop()方法無限迴圈查詢messagequeue佇列是否有訊息儲存了。有訊息就取出來呼叫dispatchmessage()方法處理。這個方法最終呼叫了我們自己重寫了訊息處理方法handlemessage(msg);這樣就完成訊息從子執行緒到主線程的無聲切換。
最後補充:我們經常使用handler沒有建立looper呼叫looper.pepare()和looper.loop()是因為在activitythread中已經建立了主線程的looper物件,儲存在了threadlocal中,我們在建立hander的時候就會從threadlocal中取出來這個looper。
從主線程類activitythread的入口方法main()方法中我們可以清楚的看到系統幫我們如何實現的:
public static void main(string args)
if (false)
// end of event activitythreadmain.
trace.traceend(trace.trace_tag_activity_manager);
looper.loop();// 開始呼叫loop()方法
throw new runtimeexception("main thread loop unexpectedly exited");
}
this到底是誰
js中函式的4種呼叫方式 1.作為普通函式來呼叫 alert window.xx undefined function t t alert window.xx 333 解釋 作為普通函式來呼叫this時,this的值指向 windwo,準確的說,this為null,但被解釋成window,在ecma...
Segmentation fault到底是何方妖孽
那麼對於任何沒有經過 mmu對映過的虛擬空間的位址,不管程序是執行寫操作還是讀操作,作業系統都會捕捉到這個錯誤的非法訪問,然後輸出乙個 segmetation fault 的錯誤提示資訊並強行終止程序。程式之所以會時不時的出現 segmetation fault 的根本原因是程序訪問到了沒有訪問許可...
孔子到底是誰?
提起孔子,想必中國人都知道,甚至比熟悉人民幣還熟悉他。因為對他的炒作已經延續了幾千年,使得這位山東大漢紅的發紫,但孔子到底是誰呢,擁有這麼大的魔力,在中華幾千年文化中處處留下他的烙印,使得 孔子門 經得起千年歲月的沖刷,依然濤聲依舊。其實孔子是被歷代帝王捧紅的,也就是說,孔子是中國被炒作最厲害的人物...