序言
handler是我們日常編碼中經常使用的一個類,通常用來由子線程轉到主線程,或子線程與子線程之前的通信(消息傳遞),那麼什麼是Looper呢?什麼是MessageQueuene呢?不要着急,我們一步一步看!
1.示例
一般我們編碼最基本的常識就是,不能在主線程執行耗時操作(如網絡請求、讀取數據、數據庫讀寫、io流操作等等),必須創建一個子線程去執行,如以下示例:
//運行在子線程
Thread twoThread = new Thread(new Runnable() {
@Override
public void run() {
Message message = mHandler.obtainMessage();
message.obj = "handler";
mHandler.sendMessage(message);
Log.i("TAG" , "當前線程twoThread:" + Thread.currentThread().getName());
}
});
twoThread.setName("thread#2");
twoThread.start();
耗時操作可以放在run方法中去執行,並且可以設定指定的線程名,但是我們執行完耗時操作拿到數據之後,需要回到主線程去更新UI,但是子線程不能更新UI,所以我們需要轉到主線程去進行UI更新操作,就需要用到handler,如以下示例:
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
我們在子線程中獲取到數據之後,通過sendMessage來發送數據,然後在handler中接收數據進行處理,基本的使用就完成了,但是我們僅僅知道使用是不夠的,我們要了解其中的原理,並且能夠在未來的編碼中使用這種模式。
到這裏我們就有一個問題了,爲什麼handler能夠接收子線程發送的Message?我們可以通過源碼解析來進行分析!
2.源碼解析
1.Looper
一般looper有兩個常用的方法Looper.prepare()和Looper.loop()
Looper.prepare():用來創建一個Looper對象,並把Looper對象存儲到sThreadLocal對象中
Looper.loop():用來輪詢,假如有新的消息,就將新的消息添加到消息隊列中;假如沒有消息,就阻塞線程
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//prepare()方法用來創建一個Looper
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
- 在prepare()方法中,先創建了一個ThreadLocal對象,相當於一個數組或者一個HashMap,用來存儲當前線程的消息,使用ThreadLocal的好處是可以保證當前拿到的消息是這個線程的消息,可以避免消息錯亂!
- 假如sThreadLocal已經有Looper對象了,將會報錯!因爲已經創建過的Looper對象不能重複添加,用來保證唯一性!這也說明了prepare()方法只能調用一次!
- 將創建的looper對象添加到sThreadLocal中
接下來我們看一下創建Looper的方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
- mQueue:消息隊列,用來保存發送的消息
- mRun :是否運行
- mThread:當前所在的線程
創建完Looper之後,我們會調用Looper.loop()方法,loop()方法重點代碼如下:
public static void loop() {
//獲取Looper對象
final Looper me = myLooper();
//Looper對象不能爲空,即必須先創建
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//從消息隊列中獲取消息列表
final MessageQueue queue = me.mQueue;
//開始進行輪詢
for (;;) {
//遍歷所有的消息
Message msg = queue.next(); // might block
//假如沒有消息,就進行阻塞
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
/*
* 如果有消息了,就通過dispatchMessage()方法進行發送消息,消息將在dispatchMessage()中
* 進行處理
*/
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//釋放資源
msg.recycleUnchecked();
}
}
我們一步一步看,首先獲取Looper對象,那麼Looper對象從哪裏來呢?我們還記得在調用Looper.prepare()方法時創建了一個Looper對象,並把它set到sThreadLocal中,那麼我們的Looper對象也很有可能是在sThreadLocal中取的,我們看一下myLooper()方法;
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
果然如此!上面的註釋已經寫得很明白了,我們總結一下loop方法到底做了什麼!
(1)獲取Looper對象,並且從消息隊列獲取消息
(2)無限循環消息,如果沒有新的消息,就阻塞線程;如果有新的消息,就通過dispatchMessage()方法處理
(3)釋放資源
msg.target返回的是一個handler對象,這個時候,我們的handler和message已經開始產生關聯了!接下來就到我們的主角Handler上場了!
2.Handler
回到最開始我們的示例代碼
Message message = mHandler.obtainMessage();
message.obj = "handler";
mHandler.sendMessage(message);
我們從子線程轉到主線程時,是通過handler來傳輸消息,首先獲取了Message的單例對象,通過sendMessage方法來發送,那麼最重要的肯定是sendMessage方法了
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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);
}
sendMessage()方法最後調用enqueueMessage()方法來解析數據
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
我們爲msg.target賦了值,在以上我們的Looper.loop()方法最後處理是調用msg.target.dispatchMessage(msg),在sendMessage時,msg.target被賦值!接下來我們看一下dispatchMessage()方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
我們可以看到很熟悉的一個方法handlerMessage()
public void handleMessage(Message msg) {
}
handlerMessage()竟然是一個空方法,仔細一想確實,我們看下面一串代碼就知道了
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
handlerMessage()方法是用來給我們實現的,在handler中重寫父類方法,拿到Message來進行接下來的操作!
我們再回頭看一下handler的構造方法
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
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;
}
在handler的構造方法中,通過獲取對應且唯一的Looper來傳遞消息,當然,一定要調用Lopper.prepare()方法來創建Looper!完整的消息傳遞機制就顯而易見了!
總結
(1)開啓線程,調用Looper.prepare()方法創建一個Looper,Looper.prepare()只能調用一次,同時將創建一個MessageQueue(消息隊列),用來存儲消息
(2)調用Looper.loop()方法來獲取Lopper對象和MessageQueue消息隊列,並開啓無限循環,讀取消息隊列的消息,如果消息隊列裏沒有消息,則進行阻塞;如果有新的消息,則通過dispatchMessage傳遞給handler
(3)創建handler接收子線程發送過來的消息,通過dispatchMessage()方法拿到Message,通過重寫父類方法handlerMessage()最終拿到數據