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