什么是Handler?
android處理用來更新UI的一套機制,也是消息處理的一套機制,可以用來發(fā)送消息,也可以用來處理消息.
多個生產(chǎn)者,一個消費者的線程模型.
為什么要用Handler
異步耗時操作,使用handler更新UI
怎么使用
發(fā)送/移除消息
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//主線程的回調(diào)
}
}
//第一種
Message message = new Message();
//...arg1,arg2,what,obj相關(guān)賦值
handler.sendMessage(message);
//第二種 復(fù)用msg
Message message = handler.obtainMessage();
//...arg1,arg2,what,obj相關(guān)賦值
message.sendToTarget();
//第三種 直接發(fā)送
handler.sendEmptyMessage();
//第四種 延遲發(fā)送
handler.sendMessageDelayed();
//第五種 指定時間
handler.sendMessageAtTime();
//第六種 插入到隊列前側(cè)
handler.sendMessageAtFrontOfQueue();
//以上msg的發(fā)送方式,都會進入handler的handleMsg()方法里
//第七種 該方法將直接調(diào)用Message內(nèi)部的Runnable(具體見handler的dispathMessage()下文在looper部分時有講到)
Message.obtain(handler, new Runnable() {
@Override
public void run() {
}
}).sendToTarget();
//移除消息
handler.removeCallBacks(runnable);
處理消息
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
//截獲msg消息 返回值true代表消息被截獲,不再向下執(zhí)行
return false;
}
}) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
為什么要設(shè)計只能通過Handler來更新UI
- 更新界面錯亂
多個線程更新UI,并且不加鎖 - 性能下降
加鎖后性能就會下降
所以通過主線程消息隊列的方式來更新UI(避免多線程并發(fā))
Handler的原理
Looper
- 內(nèi)部包含了一個消息隊列MessageQueue
- Looper.loop()方法,不斷從MQ中取消息,有就將message交給target(也就是handler),沒有就阻塞.
MessageQueue
- 消息隊列,消息的容器
Handler
內(nèi)部與Looper關(guān)聯(lián),在handler內(nèi)部可以找到looper,找到looper就是找到了MQ,
Handler負責(zé)發(fā)送消息,Looper負責(zé)接收Handler發(fā)送的消息,并把消息回傳給Handler自己.MQ就是一容器
源碼相關(guān)
Looper
- ActivityThread.main()
- Looper.PrepareMainLooper();
- Looper.prepare(quitAllowed);
- if(ThreadLocal.get() == null){ThreadLocal.set(new Looper())};
- new MessageQueue();
從ActivityThread的main()方法進入
然后調(diào)用Prepare方法,
new出Lopper對象,同時在looper的構(gòu)造函數(shù)里,new出MQ對象
然后將looper存入到ThreadLocal中
ThreadLocal (線程本地變量)
當(dāng)使用ThreadLocal維護變量時,ThreadLocal為每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應(yīng)的副本。
簡單理解為這一個對象可以為每個線程存儲不同的數(shù)據(jù)
關(guān)于ThreadLocal不錯的博文
Handler與Looper的關(guān)聯(lián)
Handler handler = new Handler();handler.mLooper = Looper.myLooper(); //return sThreadLocal.get()handler.mQueue = mLooper.mQueue;
在Handler構(gòu)造函數(shù)中,將當(dāng)前線程存儲的looper,mQueue對象.給到handler
Looper.loop()
用來開啟消息循環(huán).注意 該方法之后的代碼都將不會執(zhí)行.
為了方便觀察,移除了一些不必要代碼,并將for循環(huán),改成了while(true)
public void loop(){
while(true){
// ...
Message msg = mQueue.next();
if(msg == null){
return;
}
//這里的target就是handler自身
msg.target.dispatchMessage();
// ...
}
}
// msg.target就是handler
// 所以最終調(diào)用的handler的dispathMessage()
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//此處對應(yīng)上文handler第七種發(fā)送消息的處理
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
sendMessage(Message message)
將消息加入到MessageQueue中
-
handler.sendMessage(msg);發(fā)送消息 -
msg.target = handler;綁定target -
queue.enqueueMessage(message, uptimeMills);將消息加入MessageQueue隊列
總結(jié)
App在啟動的時候,將主線程的Lopper和MessageQueue創(chuàng)建完畢.并將looper對象存至threadLocal對象,調(diào)用looper.loop()方法, 一直循環(huán)looper對象中的messageQueue.
我們在編碼時,在主線程實例一個handler對象,就會通過threadLocal對象將主線程的looper對象取出,并將looper和mq對象與handler關(guān)聯(lián).這樣在handler.sendMessage(msg)后,就能通過looper遍歷出的msg,取出handler,來調(diào)用dispatchMessage方法
相關(guān)使用方式總結(jié)
子線程中Handler
Handler handler;
new Thread() {
@Override
public void run() {
Looper.prepare();//將該線程的looper對象存入threadLocal
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.i(TAG, "handleMessage: " + Thread.currentThread());
}
};
Looper.loop();
}
}.start();
//休眠500毫秒,待子線程handler創(chuàng)建完成
Thread.sleep(500); //可以通過HandlerThread來處理多線程并發(fā)時,對象為空的問題
//主線程通知子線程處理消息
handler.sendEmptyMessage(0);
子線程中直接調(diào)用主線程的handler
new Thread() {
@Override
public void run() {
// do something
Message.obtain(new Handler(getMainLooper()), new Runnable() {
@Override
public void run() {
Log.i(TAG, "Message.obtain " + Thread.currentThread());
}
}).sendToTarget();
}.start();