讀前思考
學習一門技術或者看一篇文章最好的方式就是帶著問題去學習,這樣才能在過程中有茅塞頓開、燈火闌珊的感覺,記憶也會更深刻。
- 如何獲取 Message 進行消息發(fā)送
- 使用 handler 造成內存泄露,如何避免?
- handler looper messageQueue的關系
- 程序是如何區(qū)分是將消息發(fā)給哪個 Handler ?
概述
做 Android 開發(fā)肯定離不開跟 Handler 打交道,它通常被我們用來做主線程與子線程之間的通信工具,而 Handler 作為 Android 中消息機制的重要一員也確實給我們的開發(fā)帶來了極大的便利。
可以說只要有異步線程與主線程通信的地方就一定會有 Handler。
1、 Handler 的基本使用
1.1 創(chuàng)建 Handler
Handler 允許我們發(fā)送延時消息,如果在延時期間用戶關閉了 Activity,那么該 Activity 會泄露。
這個泄露是因為 Message 會持有 Handler,而又因為 Java 的特性,內部類會持有外部類,使得 Activity 會被 Handler 持有,這樣最終就導致 Activity 泄露。
解決該問題的最有效的方法是:將 Handler 定義成靜態(tài)的內部類,在內部持有 Activity 的弱引用,并及時移除所有消息。
示例如下:
public class Part8HandlerActivity extends AppCompatActivity {
private Button bt_handler_send;
private static class MyHandler extends Handler {
//弱引用持有Part8HandlerActivity , GC 回收時會被回收掉
private WeakReference<Part8HandlerActivity> weakReference;
public MyHandler(Part8HandlerActivity activity) {
this.weakReference = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
Part8HandlerActivity activity = weakReference.get();
super.handleMessage(msg);
if (null != activity) {
//執(zhí)行業(yè)務邏輯
Toast.makeText(activity,"handleMessage",Toast.LENGTH_SHORT).show();
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_part8_handler);
//創(chuàng)建 Handler
final MyHandler handler = new MyHandler(this);
bt_handler_send = findViewById(R.id.bt_handler_send);
bt_handler_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
//使用 handler 發(fā)送空消息
handler.sendEmptyMessage(0);
}
}).start();
}
});
}
@Override
protected void onDestroy() {
//移除所有回調及消息
myHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}
}
注意:單純的在 onDestroy 移除消息并不保險,因為 onDestroy 并不一定執(zhí)行。
1.2 Message 獲取
獲取 Message 大概有如下幾種方式:
Message message = myHandler.obtainMessage(); //通過 Handler 實例獲取
Message message1 = Message.obtain(); //通過 Message 獲取
Message message2 = new Message(); //直接創(chuàng)建新的 Message 實例
通過查看源碼可知,Handler 的 obtainMessage() 方法也是調用了 Message 的 obtain() 方法
public final Message obtainMessage()
{
return Message.obtain(this);
}
通過查看 Message 的 obtain 方法
public static Message obtain(Handler h) {
//調用下面的方法獲取 Message
Message m = obtain();
//將當前 Handler 指定給 message 的 target ,用來區(qū)分是哪個 Handler 的消息
m.target = h;
return m;
}
//從消息池中拿取 Message,如果有則返回,否則創(chuàng)建新的 Message
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
為了節(jié)省開銷,我們在使用的時候盡量復用 Message,使用前兩種方式進行創(chuàng)建。
1.3 Handler 發(fā)送消息
Handler 提供了一些列的方法讓我們來發(fā)送消息,如 send()系列 post()系列 。
不過不管我們調用什么方法,最終都會走到 MessageQueue.enqueueMessage(Message,long) 方法。
以 sendEmptyMessage(int) 方法為例:
//Handler
sendEmptyMessage(int)
-> sendEmptyMessageDelayed(int,int)
-> sendMessageAtTime(Message,long)
-> enqueueMessage(MessageQueue,Message,long)
-> queue.enqueueMessage(Message, long);
從中可以發(fā)現 MessageQueue 這個消息隊列,負責消息的入隊,出隊。
2、Handler 原理解析
1. 構造函數
實際上我們在實例化 Handler 的時候 Handler 會去檢查當前線程的 Looper 是否存在,如果不存在則會報異常,也就是說在創(chuàng)建 Handler 之前一定需要先創(chuàng)建 Looper 。
public Handler(Callback callback, boolean async) {
//檢查當前線程是否持有 Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//Looper 持有一個 MessageQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
2. Looper
通過調用 Looper.prepare() 可以在當前線程創(chuàng)建 Looper,然后調用 Looper.loop() 讓消息隊列循環(huán)起來。
代碼如下:
private static void prepare(boolean quitAllowed) {
//如果當前線程已經存在 Looper,則會拋出異常
//在主線程中調用 prepare 就會拋出此異常,因為主線程已經存在 Looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//將創(chuàng)建的 Looper 對象存入線程的 ThreadLocal 中,保持唯一
sThreadLocal.set(new Looper(quitAllowed));
}
Looper.loop() 相關代碼
public static void loop() {
//會先獲取當前線程的 Looper,如果不存在拋出異常
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;
//..
for (;;) {
//不斷從 MessageQueue 中獲取消息
Message msg = queue.next(); // might block
//
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//..
try {
// 通過 handler 發(fā)送消息
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
//..
}
//..
//回收 Message
msg.recycleUnchecked();
}
}
3. 小結
Handler 的背后有著 Looper 以及 MessageQueue 的協(xié)助,三者通力合作,分工明確。
嘗試小結一下它們的職責,如下:
Looper :負責關聯(lián)線程以及消息的分發(fā)在該線程下從 MessageQueue 獲取 Message,分發(fā)給 Handler ;
MessageQueue :是個隊列,負責消息的存儲與管理,負責管理由 Handler 發(fā)送過來的 Message ;
Handler : 負責發(fā)送并處理消息,面向開發(fā)者,提供 API,并隱藏背后實現的細節(jié)。
Handler 發(fā)送的消息由 MessageQueue 存儲管理,并由 Loopler 負責回調消息到 handleMessage()。
線程的轉換由 Looper 完成,handleMessage() 所在線程由 Looper.loop() 調用者所在線程決定。
3、 總結
Handler 簡單易用的背后藏著工程師大量的智慧,要努力向他們學習。
希望看完本文能加深你對 Handler 的理解,對接下來學習有所幫助。