1.最常見的使用場景
Android中常用Handler使用場景?
并不能在子線程中通路UI控件,否則會觸發程式異常,這時候需要通過Handler将更新UI的操作切換到主線程進行
系統為什麼不允許在子線程中通路UI呢?
UI控件不是線程安全的,多線程并發通路的時候可能會導緻UI處于不可預期的狀态,如果對UI控件的通路加線程鎖,會降低UI線程的通路效率,另外就是邏輯變的複雜。
簡要概括:
Handler運作需要底層MessageQueue 和 Looper支撐
MessageQueue:消息隊列,内部存儲一組消息,一對隊列的形式對外提供插入和删除的工作。内部存儲結構不是隊列,而是單連結清單
Looper:消息循環,MessageQueue隻是消息的存儲單元,不能處理消息,Looper則無限循環查詢消息,如果有新消息,則處理,否則一直等待。
ThreadLocal:并不是線程,它的作用是在每個線程中存儲資料。
Handle建立的時候會才用目前線程的Looper來構造消息循環系統,Handler是怎麼獲得目前線程的呢?是因為使用了ThreadLocal,它可以在不同的線程中存儲并提供資料,通過ThreadLocal可以輕松擷取每個線程的Looper。
ThreadLocal詳解
ThreadLocal是一個線程内部存儲類,通過他可以在指定的線程中存儲資料,資料存儲以後,隻有在指定線程中可以擷取到存儲的資料,對于其他線程則無法擷取到資料。
1.使用場景:
①以線程為作用域并且不同的線程具有不同的資料副本。
②複雜邏輯下的對象傳遞,例如監聽器的傳遞。通過參數傳遞或者靜态全局變量,都有局限性
2.使用
ThreadLocal<T> mtL = new ThreadLocal<T>();
//設定
mtl.set("..");
//擷取
mtl.get();
3.源碼解析
public void set(T value) {
Thread currentThrea = Thread.currentThread();
Values values = values(currentThread);
if(values == null) {
values = initializeValues(currentThrea);
}
values.put(this,value);
}
通過valuse方法擷取目前線程中的ThreadLocal資料,沒有則初始化,然後再講ThreadLocal的值進行存儲,數組形式的存儲,這裡數組排列順序:所有的線程有一個排序,這個排序,存儲資料是目前線程排序下角标加一。具體就不深入了~
Looper詳解
new Thread(new Runnable() {
@Override
public void run() {
//為目前線程建立looper
Looper.prepare();
Handler handler = new Handler();
//開啟消息循環
Looper.loop();
}
}).start();
①Looper.quit()和Looper.quitSafely()的差別
quit會直接退出Looper,而quitSafely隻是設定一個退出标記,然後把消息隊列中的已有的消息處理完畢才安全的退出。
解析:
①Looper.prepare()
此方法會調用Looper(boolea quitAllowed)建立Looper,
private Looper(boolea quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
持有目前線程和MessageQueue;
②Looper.loop()
代碼過于長就不粘貼了,大概解析一下
....
for(;;){
Message msg = mQueue.next();
if(msg == null) {
return;
}
...
msg.target.dispatchMessage(msg);
...
}
loop方法是個死循環,唯一跳出的方式就是MessageQueue的next方法傳回null,即當調用Looper.quit()和Looper.quitSafely(),next為空,消息隊列退出。也就是說,如果Looper必須退出嗎,否則一直循環。
處理消息:msg.target.dispatchMessage(msg);其中msg.target是發送這條消息的Handler的對象,也就是說,消息最終交給dispatchMessage方法處理
Handler的工作原理
handler.sendMessage(Message msg); –> sendMessageDelayed(Message msg,long 0); –> sendMessageAtTime(Message msg, long delaymillis) –> enqueueMessage(MessageQueue queue, Message msg, long delaymillis)
Handler發送消息是向消息隊列中插入一條消息,MessageQueue的next方法就會傳回這條消息給Looper,Looper收到消息後就開始處理了,最終消息由Looper交由Handler處理,即Handler的dispatchMessage方法會被調用,這時Handler就進入處理消息階段了,調用handleCallBack(msg)
抽象方法
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
Handler和Handler.Callback的handleMessage差別:
如果直接用Handler的handleMessage黃色警報,換成靜态的就好了,原因:MessageQueue中的消息隊列會一直持有對handler的引用,而作為内部類的handler會一直持有外部類的引用,就會導緻外部類不能被GC回收。當我們發延時很長的msg時就容易出現洩漏。是以此處應該設定為static,然後Handler就會跟随類而不是跟随類的對象加載,也就不再持有外部類的對象引用。
handler.post(new Runnable(){
@Override
public void run() {
mTestTV.setText("This is post");//更新UI
}});
和handler.sendMessage(msg);作用相當