1.為什么要引入Handler機制践瓷?
舉個栗子:通過網(wǎng)絡(luò)獲取數(shù)據(jù)然后顯示在TextView中耕驰,由于網(wǎng)絡(luò)通信屬于耗時操作炸卑,所以必須在子線程中完成既鞠,但是子線程中是不能更新UI的(特殊情況除外),為了解決以上問題盖文,Android引入了Handler機制嘱蛋,由Handler來負責與子線程進行通信,從而使子線程與主線程之間建立起協(xié)作的橋梁五续,使Android的UI更新問題得到完美的解決洒敏。
注:Android系統(tǒng)是在onResume方法回調(diào)之后檢查當前線程的,在此之前是可以在子線程中更新UI的疙驾,Google并不建議這么做凶伙,了解就好。
2.Handler原理
看圖說話:
先說說幾個重要的類:
Message:消息它碎,由MessageQueue統(tǒng)一隊列函荣,然后交由Handler處理。
MessageQueue:消息隊列扳肛,用來存放Handler發(fā)送過來的Message傻挂,并且按照先入先出的規(guī)則執(zhí)行。
Handler:處理者挖息,負責發(fā)送和處理Message金拒。
Looper:消息輪詢器,不斷的從MessageQqueue中抽取Message并執(zhí)行旋讹。
1.ActivityThread在主線程中啟動消息循環(huán)器Looper
在創(chuàng)建Activity之前殖蚕,當程序啟動的時候,系統(tǒng)會先加載ActivityThread這個類沉迹,在這個類的main函數(shù)中睦疫,調(diào)用Looper.prepareMainLooper()初始化Looper對象并創(chuàng)建消息隊列,然后調(diào)用Looper.loop()方法鞭呕,不斷的輪詢消息隊列中的消息蛤育。
package android.app;
public final class ActivityThread {
public static void main(String[] args) {
...
// 初始化Looper對象并創(chuàng)建消息隊列
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// 開啟消息輪詢
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
看下Looper.prepareMainLooper()與Looper.loop()方法中發(fā)生了什么:
package android.os;
public final class Looper {
/**
* 為當前線程初始化Looper對象并創(chuàng)建消息隊列
* 消息循環(huán)可以被終止
*/
public static void prepare() {
prepare(true);
}
/**
* 為當前線程初始化Looper對象并創(chuàng)建消息隊列
*/
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 隊列與線程進行關(guān)聯(lián),防止其他線程對其訪問
sThreadLocal.set(new Looper(quitAllowed));
}
/**
* 為UI線程初始化Looper對象并創(chuàng)建消息隊列
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
/**
* 啟動消息輪詢
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 獲取消息隊列
final MessageQueue queue = me.mQueue;
// 消息循環(huán)
for (;;) {
// 獲取消息隊列中的消息,可能會阻塞
Message msg = queue.next();
if (msg == null) {
return;
}
try {
// 處理消息
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
// 回收消息
msg.recycleUnchecked();
}
}
/**
* 獲取當前線程的Looper對象
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
/**
* 獲取當前線程的消息隊列
*/
public static @NonNull MessageQueue myQueue() {
return myLooper().mQueue;
}
/**
* Looper構(gòu)造方法瓦糕,初始化消息隊列與當前線程
*/
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
}
到這里底洗,Handler發(fā)送和接收消息的準備工作就已經(jīng)完成了,接下來讓我們來初始化一個Handler試試吧咕娄!
2.在主線程中創(chuàng)建Handler
通常我們都會在主線程中創(chuàng)建Handler來接收子線程的消息亥揖,看下Handler是如何創(chuàng)建的:
// 定義一個Handler對象,并實現(xiàn)handleMessage方法
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 在此接收子線程發(fā)送的消息
}
};
Handler中的構(gòu)造方法最終都會調(diào)用Handler(Callback callback, boolean async)方法圣勒,獲取當前線程的Lopper對象费变,與之關(guān)聯(lián),然后獲取消息隊列圣贸。
package android.os;
public class Handler {
public Handler(Callback callback, boolean async) {
...
// 獲取當前線程的Looper對象
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// 獲取消息隊列
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
}
3.使用sendMessage發(fā)送消息
package android.os;
public class Handler {
/**
* 發(fā)送消息
*/
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
/**
* 延時發(fā)送消息
*/
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
// 計算消息入列的時間
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
/**
* 發(fā)送定時消息
*/
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);
}
/**
* 消息入列
*/
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
}
看下enqueueMessage方法:
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
// 如果有新消息首次加入隊列或者新消息的延遲時間小于隊列中首個消息的延遲時間挚歧,就將新消息放在隊列頭部
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 如果隊列不為空,將新消息按照時間排序放入隊列
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p;
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
4.消息隊列將消息分發(fā)給Handler處理
發(fā)送消息到消息隊列后吁峻,Looper就會在消息隊列中按順序取出消息分發(fā)給Handler處理滑负,看下dispatchMessage方法,如果callback對象為null的話用含,就會回調(diào)handleMessage方法矮慕,如果不為空會回調(diào)callback的run方法。我們平時使用的sendMessage方法沒有設(shè)置callback啄骇,所以會回調(diào)handleMessage方法凡傅,如果使用post(Runnable callback)方法,則會回調(diào)callback的run方法肠缔。
package android.os;
public class Handler {
/**
* 消息分發(fā)
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 回調(diào)callback的run方法
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 回調(diào)handleMessage方法
handleMessage(msg);
}
}
}
OK夏跷,通過對源碼的分析,Handler的原理已經(jīng)學習完了明未,接下來讓我們來學習一下如何在子線程中創(chuàng)建Handler槽华。
3.在子線程中創(chuàng)建Handler
通常我們使用Handler都是從子線程向主線程發(fā)送消息,如果需要主線程通知子線程做一些耗時邏輯趟妥,或者子線程之間進行通信的話猫态,直接在子線程中創(chuàng)建Handler會拋出異常:
Can't create handler inside thread that has not called Looper.prepare()
看下源碼:
package android.os;
public class Handler {
public Handler(Callback callback, boolean async) {
...
// 獲取當前線程的Looper對象
mLooper = Looper.myLooper();
// 如果looper為空則會拋出異常
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// 獲取消息隊列
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
}
由于每個線程都用自己的Looper,這個Looper可以為null披摄,系統(tǒng)默認在主線程中創(chuàng)建了Looper亲雪,但在子線程中需要手動設(shè)置,否則就會拋出異常疚膊。
代碼實現(xiàn):
new Thread("子線程") {
@Override
public void run() {
super.run();
// 初始化Looper對象并創(chuàng)建消息隊列
Looper.prepare();
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.i(TAG, (String) msg.obj + "___" + Thread.currentThread().getName());
}
};
Message message = new Message();
message.obj = "消息";
handler.sendMessage(message);
// 開始輪詢
// 由于loop方法是死循環(huán)义辕,所以要寫在最后
Looper.loop();
}
}.start();
還有沒有更優(yōu)雅的方式呢?答案是肯定的寓盗,為了解決上面的問題灌砖,Android系統(tǒng)為我們提供了HandlerThread類璧函,看下源碼:
package android.os;
public class HandlerThread extends Thread {
...
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
// 初始化Looper對象并創(chuàng)建消息隊列
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
// 開啟消息輪詢
Looper.loop();
mTid = -1;
}
}
有沒有很熟悉,HandlerThread類的run方法中做了和ActivityThread類中一樣的處理基显,這樣就不用再手動初始化Looper了蘸吓,nice,接下來讓我們用HandlerThread來實現(xiàn)一下:
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.i(TAG, (String) msg.obj + "___" + Thread.currentThread().getName()); }
};
Message message = new Message();
message.obj = "消息";
handler.sendMessage(message);
4.寫在最后
歡迎同學們吐槽評論撩幽,如果你覺得本篇博客對你有用库继,那么就留個言或者點下喜歡吧(^-^)