Handler是我們常用的一種通信方式,可用于子線程更新UI
對于Handler我們需要知道的有以下四種對象
- Handler:用于分發(fā)消息
- MessageQueue:用于存儲Message
- Message:通信的消息
- Looper:一個消息循環(huán)機(jī)制
一:Handler的基本使用
我們使用Handler的時候,一般是這么使用的(有多種使用方式,這里簡單的舉例)
// MainActivity.kt
val handler: Handler = Handler(
Handler.Callback {
when (it.what) {
1 -> {
// doSomething
}
else -> {}
}
true
}
)
// 在其他地方調(diào)用
val obtain = Message.obtain()
obtain.what = 1
handler.sendMessage(obtain)
先新建一個Handler,并聲明回調(diào),在回調(diào)中根據(jù)傳遞的數(shù)據(jù)做不同的動作,之后在任何一個地方都可以使用handler對象的sendMessage()方法,發(fā)送數(shù)據(jù),數(shù)據(jù)會在之前聲明的回調(diào)中接收到
二:源碼分析
為什么我們的Handler可以這么使用呢?
對于這個問題,我覺得我們學(xué)習(xí)Android的有必要知道一下,起碼以后面試可能也有用處。
我們可以一步一步來分析
首先我們使用Handler就必須創(chuàng)建一個Handler,我們查看Handler的構(gòu)造函數(shù)
// Handler.java
// 本例用的即此構(gòu)造函數(shù)
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper(); // 1
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
此時我們可以看到1位置上Looper.myLooper()這是去獲取Looper對象,Looper是一個循環(huán)機(jī)制,可以不斷的從隊列中拿到消息進(jìn)行發(fā)送,我們來看下這個對象是怎么獲取到的
// Looper.java
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
看到這里,可能大家會疑惑這sThreadLocal是什么鬼,其實這是一個ThreadLocal對象,我們可以簡單理解下,就是它可以讓我們每個線程都有一個屬于自己的局部變量,隸屬于線程,在當(dāng)前線程中使用sThreadLocal.get()都可以拿到線程中唯一的局部變量,可能介紹的不是那么容易懂,其實它的源碼也很簡單,有興趣的大家可以去看看,絕對讓你一下子就能理解的。
注意:使用
Looper也是有規(guī)定的,需要先調(diào)用Looper.prepare(),然后才能調(diào)用Looper.loop(),為什么?請看下面的解釋
一般我們使用Looper,我們?nèi)绻胍屪泳€程一直運(yùn)行,并隨時在子線程中處理數(shù)據(jù)
則可以這么做
Thread {
Looper.prepare()
mThreadHandler = Handler(Looper.myLooper()) {
// 處理數(shù)據(jù)
true
}
Looper.loop()
}.start()
如果我們不使用Looper.prepare(),繼而使用Looper.loop()則程序會報錯
// Looper.java
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
...
}
那么問題來了,我們的Handler是在主線程實現(xiàn)的,既然能一直接收消息,證明主線程是有Looper存在,并且執(zhí)行了Looper.prepare()與Looper.loop(),不然程序就會奔潰,那么這玩意到底是怎么在哪里創(chuàng)建出來的呢?
答案是在ActivityThread類的main函數(shù)創(chuàng)建出來的
// ActivityThread.java
public static void main(String[] args) {
...
Looper.prepareMainLooper(); // 1
...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop(); // 2
throw new RuntimeException("Main thread loop unexpectedly exited");
}
可以看到這里的1和2分別調(diào)用了我們想要看到的Looper.prepare()與Looper.loop(),至于這個ActivityThread是什么,我們現(xiàn)在只需要知道它就是一個手機(jī)中每個應(yīng)用的入口,正如我們運(yùn)行Java程序時需要調(diào)用main()入口函數(shù)一樣,這個類中也提供了一個main()函數(shù),就是應(yīng)用的入口函數(shù),在這個入口函數(shù)中,就創(chuàng)建了我們需要的主線程Looper對象,所以我們直接在主線程實例化Handler對象就是使用的這個Looper對象,并可以一直接收消息
既然Looper循環(huán)有了,Handler就能一直接收消息了。
接下來我們從發(fā)送數(shù)據(jù)開始看起
val obtain = Message.obtain()
obtain.what = 1
handler.sendMessage(obtain)
以上就是創(chuàng)建一個Message然后發(fā)送出去,我們可能也常用handler.post(Runnable r),其實查看源碼可以得知不管是用哪一種方式去發(fā)送數(shù)據(jù),最后調(diào)用到Handler對象的一個方法
// Handler.java
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; // 1
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到1這里需要發(fā)送的msg的target指定了這個Handler(這里要記住),然后再用這個queue發(fā)送一個數(shù)據(jù)出去
那么這里又疑惑了,這個queue又是哪里來的,也就是Handler的mQueue對象從哪里來,其實上面的代碼就已經(jīng)提示了,在Handler的初始化構(gòu)造函數(shù)里面就有
// Handler.java
mQueue = mLooper.mQueue;
這時候我們可以理一下就是,Looper一直循環(huán),Looper里面有一個消息隊列MessageQueue,發(fā)送數(shù)據(jù)都是通過這個Looper里面的一個對象mQueue發(fā)送的
接下來我們看這個消息隊列是怎么發(fā)數(shù)據(jù)的,通過queue.enqueueMessage(msg, uptimeMillis);我們?nèi)タ聪?code>MessageQueue類里面的enqueueMessage方法
// MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
// 1,先獲取當(dāng)前準(zhǔn)備要發(fā)送的消息
Message p = mMessages;
boolean needWake;
// 2,如果當(dāng)前要準(zhǔn)備的消息是空的,或者我們設(shè)置的時間是0
// 意思就是此時消息隊列里面是空的,或者我們設(shè)置的時間小于這個當(dāng)前準(zhǔn)備發(fā)送的消息
// 則交換下順序,把發(fā)送的消息和當(dāng)前準(zhǔn)備發(fā)送的消息調(diào)換一下順序,即先發(fā)我們要發(fā)送的消息
if (p == null || when == 0 || when < p.when) { // 2
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
// 3,如果不能插入到當(dāng)前準(zhǔn)備發(fā)送的消息前面,則往后面依次進(jìn)行排序,根據(jù)這個when時間
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
代碼中標(biāo)明了注釋,仔細(xì)品品就知道這個queue.enqueueMessage(msg, uptimeMillis);,與其說是發(fā)送數(shù)據(jù),更確切的應(yīng)該說是對消息根據(jù)時間進(jìn)行排序
那么我們真正的發(fā)送消息是在哪里呢?
我們知道Looper是一個可以帶有循環(huán)機(jī)制的東西,Looper.loop即開始進(jìn)行循環(huán),我們可以看下這里面的代碼,這個方法里面有很長代碼,大家要挑重點看
// Looper.java
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;
...
// 1,此時一直循環(huán)從隊列中獲取消息
for (;;) {
// 2,獲取消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
try {
// 3,開始分發(fā)消息,此時的target即Handler
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
}
}
我們看代碼注釋2這個地方,調(diào)用queue.next,取出一個消息,然后注釋3這塊就是使用調(diào)用消息里面的target對象,由前面的代碼分析可以知道這個target就是發(fā)送消息的handler,此時就把消息分發(fā)出去了,接下來我們可以查看下這個MessageQueue的next方法,和Handler的dispatchMessage方法
MessageQueue的next方法
// MessageQueue.java
Message next() {
...
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 1,nativePollOnce 方法用于“等待”, 直到下一條消息可用為止,當(dāng)沒有消息的時候會阻塞住
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 一般的,我們的msg.target是不會為空的,所以可以略過這條判斷
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
// 2,開始取消息
if (msg != null) {
// 情況一:還沒到消息需要發(fā)送的時間,則等待這個間隔時間
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
}
// 情況二:到了發(fā)送的時間了,則把下一個需要發(fā)送的消息賦值給mMessage
// 并返回這個時候需要發(fā)送的消息
else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
// 返回需要發(fā)送的消息
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
...
}
}
}
這個方法注釋寫了很清楚,總的來說就是沒有消息則阻塞等待,有消息的就取出消息
Handler的dispatchMessage方法
// Handler.java
public void dispatchMessage(Message msg) {
// 一般我們還可以使用Handler.post(Runnable r)方法發(fā)送消息
// 這個時候的msg.callback 就是這個 r
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
這個方法就簡單了,如果我們設(shè)置了回調(diào),則會回調(diào)出去,具體執(zhí)行什么方法要看你怎么發(fā)送這個消息。
上面我們舉的例子是這樣發(fā)的
val obtain = Message.obtain()
obtain.what = 1
handler.sendMessage(obtain)
如果使用上面的這個handler.sendMessage(Message msg)則會傳消息給我們初始化的時候的回調(diào)函數(shù)
val handler: Handler = Handler(
Handler.Callback {
when (it.what) {
1 -> {
// doSomething
}
else -> {}
}
true
}
)
還有另外一種發(fā)送消息的方法,就是使用post(Runnable r)發(fā)送
handler?.post {
object : Runnable {
override fun run() {
}
}
}
我們可以看下這個post方法
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
可以看到這里有個賦值,m.callback = r,所以我們post發(fā)送數(shù)據(jù)后對應(yīng)的就是運(yùn)行這個r
public void dispatchMessage(Message msg) {
// 一般我們還可以使用Handler.post(Runnable r)方法發(fā)送消息
// 這個時候的msg.callback 就是這個 r
if (msg.callback != null) {
handleCallback(msg);
} else {
...
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
直接運(yùn)行r的run方法
解決了上面兩種數(shù)據(jù)接收方式,還剩下一個handleMessage(msg)
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
...
handleMessage(msg);
}
}
這個用于我們創(chuàng)建一個類,繼承自Handler
class Myhandler : Handler() {
override fun handleMessage(msg: Message?) {
}
}
這個時候使用如下方式發(fā)送消息,則會在上面重寫的方法handleMessage中接收到消息
val obtain = Message.obtain()
obtain.what = 1
myHandler.sendMessage(obtain)
分析到這,基本算是把Handler機(jī)制給理清了,看下來可能有點吃力,不過如果大家自己跟著源碼一步一步走下去,自己也是可以理解清楚的
總結(jié)
Handler機(jī)制:
1,Looper消息循環(huán)機(jī)制 創(chuàng)建
使用Looper.prepare(),創(chuàng)建一個Looper實例,并保存在Looper的一個靜態(tài)變量sThreadLocal中,然后用Looper.loop()開啟消息循環(huán),此時創(chuàng)建的Handler可以傳指定的Looper實例進(jìn)去,如果是主線程的創(chuàng)建的Handler,則已經(jīng)有Looper實例了,此時就不需要創(chuàng)建Looper實例
2,Handler發(fā)送消息
使用Handler.sendMessage(Message msg),或者Handler.post(Runnable r)發(fā)送消息,此時會調(diào)用Handler的enqueueMessage(),里面調(diào)用了MessageQueue的enqueueMessage,此時就把消息根據(jù)發(fā)送時間進(jìn)行了排序
3,Looper.loop() 循環(huán),檢測消息并發(fā)送
真實的發(fā)送放在了Looper.loop()方法里面,此時會調(diào)用Message.next()方法,獲取一個消息,如果沒有消息,next()方法則會阻塞等待,釋放CPU,一旦有消息,則會喚醒,并在Looper.loop()方法里面進(jìn)行消息的發(fā)送,然后消息發(fā)送又調(diào)用了Handler.dispatchMessage()方法,此時我們就可以接收到消息了