前言
Handler是安卓開發(fā)中我們常用的,主要用于線程之間的通訊,本文通過handler的幾個常用方法來分析下具體的源碼實現(xiàn)
常規(guī)操作
//創(chuàng)建消息
Message msg = Message.obtain();
msg.what=101;
msg.obj="send message";
//發(fā)送消息
mHandler.sendMessage(msg);
//接受消息
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
LogUtils.LogE(msg.toString());
}
};
這種寫法是我們用的最多的寫法,下面來具體介紹它的實現(xiàn)原理
主要方法
- 無論是sendMessage或sendMessageDelayed或链,sendEmptyMessageDelayed最終調(diào)用的方法都是sendMessageAtTime方法,sendMessageAtTime調(diào)用的是MessageQueue中的enqueueMessage方法,下面是核心代碼:
//MessageQueue中:
boolean enqueueMessage(Message msg, long when) {
//省略片段代碼
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; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
- MessageQueue類似于鏈表結(jié)構(gòu)
- if (p == null || when == 0 || when < p.when):p為等待執(zhí)行的message隊列when為我們傳入delay的時間值如果mMessages對象為空猫十,或者when為0也就是立刻執(zhí)行,或者新消息的when時間比mMessages隊列的when時間還要早
- 符合以上一個條件就把新的msg插到mMessages的前面 并把next指向它,也就是msg會插進(jìn)隊列的最前面想际,等待loop的輪詢
- 如果上述所有條件都不符合就進(jìn)入else
- else中是一個死循環(huán),when是指新消息的執(zhí)行時間,p.when是指隊列中message消息的執(zhí)行時間
- 如果新消息的執(zhí)行時間比隊列里面的消息執(zhí)行時間早就跳出循環(huán),執(zhí)行 msg.next = p;prev.next = msg,也就是插入到隊列前面先執(zhí)行它
Handler怎么與looper還有MessageQueue關(guān)聯(lián)的呢?
在ActivityThread.main() 方法中有如下代碼:
public static void main(String[] args) {
//省略若干代碼
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
}
- 原來在 ActivityThread 里 調(diào)用了 Looper.prepareMainLooper() 方法創(chuàng)建了 主線程的 Looper ,并且調(diào)用了 loop() 方法运杭,所以我們就可以直接使用 Handler 了
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));
}
- 這里是looper與當(dāng)前線程進(jìn)行綁定,prepare方法不能調(diào)用二次,這也代表了一個線程只能有一個looper
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
- 這里MessageQueue是與當(dāng)前的looper關(guān)聯(lián)
最后調(diào)用loop方法不斷去輪詢MessageQueue中的消息
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;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
}
final long newIdent = Binder.clearCallingIdentity();
msg.recycleUnchecked();
}
}
- me為 null就拋出異常,也就是必須先調(diào)用prepare方法后才能調(diào)用loop方法
- 不管什么Handler在什么線程發(fā)消息,接受消息的線程都是Looper.loop()所調(diào)用的線程
- 拿到looper中的隊列進(jìn)入死循環(huán)
- 不斷取出消息,如果沒有消息就return
- 最后調(diào)用msg.target.dispatchMessage也就是Handler來分發(fā)消息
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
- msg 沒有 callback 的話,那么將會判斷 mCallback 是否為空,這個 mCallback 就是構(gòu)造方法種傳遞的那個 Callback,優(yōu)先執(zhí)行
- 如果 mCallback為空,那么就調(diào)用 Handler 的 handleMessage(msg) 方法入蛆,否則就調(diào)用 mCallback.handleMessage(msg) 方法,然后根據(jù) mCallback.handleMessage(msg)的返回值判斷是否攔截消息硕勿,如果攔截(返回 true)
- mCallback.handleMessage(msg)返回true,Handler的handleMessage將不會調(diào)用
問題&解答
主線程為什么不用創(chuàng)建Looper?
- 因為在ActivityThread.main()方法中已經(jīng)把Looper給我們創(chuàng)建好了并且調(diào)用了loop方法
- 所有我們在主線程使用handler不用調(diào)用Looper,handleMessage回調(diào)在主線程(Looper.loop()方法在主線程)
子線程怎么使用Handler?
new Thread(){
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) { }
};
handler.sendEmptyMessage(11);
Looper.loop();
}
}.start();
- 必須調(diào)用Looper.prepare()再調(diào)用 Looper.loop()方法輪詢消息,否則會拋出 RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.")
handler#postRunnable方法是在新的線程嗎?
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
private static void handleCallback(Message message) {
message.callback.run();
}
- post方法最終調(diào)用的都是這個方法,把Runnable賦值給Message.callback
- 最終走到handleMessage方法中調(diào)用handleCallback方法即調(diào)用post方法里傳遞的 Runnable 對象的run()方法
- 總結(jié):只是一個回調(diào)方法與線程無關(guān)
handler內(nèi)存泄漏?
- 這是我們使用handler常見的警告提醒,原因是內(nèi)部類持有外部類引用,從而外部類銷毀時不能被回收而導(dǎo)致內(nèi)存泄漏
解決方法
- 在onpause方法中removeCallbacksAndMessages(null),移除消息
- 使用static加弱引用的方法
private static class MyHandler extends Handler {
private WeakReference<StudyActivity> mReference;
public MyHandler(StudyActivity activity) {
mReference = new WeakReference<StudyActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
StudyActivity activity = mReference.get();
if (activity != null) {
LogUtils.LogE(msg.what);
}
}
}
總結(jié)
- Handler 的創(chuàng)建需要先調(diào)用 Looper.prepare() 枫甲,然后再手動調(diào)用 loop()方法開啟循環(huán)
- App 啟動時會在ActivityThread.main()方法中創(chuàng)建主線程的 Looper ,并開啟循環(huán)源武,所以主線程使用 Handler 不用調(diào)用looper
- Callback.handleMessage() 的優(yōu)先級比 Handler.handleMessage()要高
- Handler.post(Runnable)傳遞的 Runnale 對象并不會在新的線程執(zhí)行
- 可以通過removeCallbacksAndMessages(null)或者靜態(tài)類加弱引用的方式防止內(nèi)存泄漏
- handlermessage回調(diào)是在Looper.loop()所在線程