Handler 淺析
Handler
一、簡介
Handler是用來結合線程的訊息佇列來發送、處理“Message物件”和“Runnable物件”的工具。每一個Handler例項之後會關聯一個執行緒和該執行緒的訊息佇列。當你建立一個Handler的時候,從這時開始,它就會自動關聯到所在的執行緒/訊息佇列,然後它就會陸續把Message/Runnalbe分發到訊息佇列,並在它們出隊的時候處理掉。簡單說,它是一套 Android 訊息傳遞機制。
二、用途
- 延時操作。推送未來某個時間點將要執行的Message或者Runnable到訊息佇列;
- 執行緒之間的通訊。將訊息推送到相應執行緒的訊息佇列中,等待處理。
三、使用方法
(1)sendMessage
// ************不配合執行緒使用************* // 獲取一個Message物件,設定what為1 Message msg = Message.obtain(); msg.obj = data; msg.what = IS_FINISH; mHandler.sendMessage(msg); // 接受並處理 mHandler = new Handler() { @Override public void handleMessage(Message msg) { tx.setText(msg.what + ""); } }; // ************配合執行緒使用************* private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == 1) { Toast.makeText(MainActivity.this, "重新整理UI、", Toast.LENGTH_SHORT).show(); } } }; new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); mHandler.sendEmptyMessage(1); } catch (InterruptedException e) { e.printStackTrace(); } } }).start();
常用 API:
- boolean sendMessage (Message msg) : 傳送訊息。
- boolean sendEmptyMessage (int what) : 直接拿到一個空的訊息,並賦值what,然後傳送到MessageQueue。
- boolean sendMessageDelayed (Message msg, long delayMillis) : 在延遲delayMillis毫秒之後傳送一個Message。
- boolean sendMessageAtTime (Message msg, long uptimeMillis) : 在某個時間點執傳送訊息。
- void removeMessages (int what) : 移除所有what值得Message物件。
(2)post
// ************不配合執行緒使用************* private Handler mHandler = new Handler(); handler.post(new Runnable() { @Override public void run() { tvMessage.setText("使用Handler.post在工作執行緒中傳送一段執行到訊息佇列中,在主執行緒中執行。"); } }); // ************配合執行緒使用************* new Thread(new Runnable() { @Override public void run() { // 在子執行緒中例項化Handler同樣是可以的,只要在建構函式的引數中傳入主執行緒的Looper即可 Handler handler = new Handler(Looper.getMainLooper()); // 通過Handler的post Runnable到UI執行緒的MessageQueue中去即可 handler.post(new Runnable() { @Override public void run() { // 在MessageQueue出隊該Runnable時進行的操作 tvMessage.setText("使用Handler.post在工作執行緒中傳送一段執行到訊息佇列中,在主執行緒中執行。"); } }); } }).start();
常用 API:
- boolean post (Runnable r) : 將Runnable物件加入MessageQueue。
- boolean postAtTime (Runnable r, Object token, long uptimeMillis) : 在某個時間點執行Runnable r。
- boolean postDelayed (Runnable r, long delayMillis) : 當前時間延遲delayMillis個毫秒後執行Runnable r。
- void removeCallbacks (Runnable r, Object token) : 移除MessageQueue中的所有Runnable物件。
- void removeCallbacksAndMessages (Object token) : 移除MessageQueue中的所有Runnable和Message物件。
四、原理
Handler 的初始化,其實是初始化 Looper 和 MessageQueue。通過 Looper.Prepare 例項化 Looper 和 MessageQueue ,並將 Looper 設定進 ThreadLocal(所以,使用 Handler 之前一定要呼叫 Looper.Prepare)。ThreadLocal.set 方法的作用是將設定的值(這裡是當前初始化的 Looper)與當前執行緒進行繫結(當前執行緒就是呼叫 Looper.prepare 的執行緒)。主執行緒中沒有顯式的呼叫 Looper.Prepare 是因為,Android 系統已經幫我們呼叫了 Looper.Prepare。
Handler 在呼叫 sendMessage 的時候主要做了兩件事:1.將自己設定給 Message 的 Target 變數,從而將 Handler 與 Message 繫結在一起;2.將 Message 放入 MessageQueue 佇列中。
Looper.loop 從 MQ 中輪訓獲取訊息,訊息不為 Null 則回撥 disPatchMessage 方法(Runnable 物件 OR handlerMessage)。
總結:
- Handler : 負責傳送和處理訊息。
- Message : 用來攜帶需要的資料。
- MessageQueue : 訊息佇列,用來儲存 Message 的。
- Looper : 訊息輪巡器,負責不停的從 MessageQueue 中取 Message。
子執行緒中使用 Handler:
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }
五、Handler 引發的記憶體洩漏
外部類Activity中定義了一個非靜態內部類Handler,非靜態內部類預設持有對外部類的引用。如果外部Activity突然關閉了,但是MessageQueue中的訊息還沒處理完,那麼Handler就會一直持有對外部Activty的引用,垃圾回收器無法回收Activity,從而導致記憶體洩漏。
解決方法:
1.停掉執行緒(切斷了與 Activity 之間的關聯)或移除 removexxx 訊息;
2.將Handler宣告為靜態類。改成靜態內部類後,對外部類的引用設為弱引用,在垃圾回收時,會自動將弱引用的物件回收。如:
public class HandlerActivity extends AppCompatActivity { private final MyHandler mHandler = new MyHandler(this); private static final Runnable mRunnable = new Runnable() { @Override public void run() { // 操作 } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fourth); mHandler.postDelayed(mRunnable, 1000*10); finish(); } private static class MyHandler extends Handler { WeakReference<HandlerActivity> mWeakActivity; public MyHandler(HandlerActivity activity) { this.mWeakActivity = new WeakReference<HandlerActivity>(activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); final HandlerActivity mActivity = mWeakActivity.get(); if (mActivity != null) { // 處理訊息 } } } }
六、HandlerThread
HandlerThread 是一種執行緒,它和普通的 Thread 之間的區別就是 HandlerThread 在建立的時候會提供自己該執行緒的 Looper 物件,不需要手動建立 Looper。
HandlerThread handlerThread = new HandlerThread("downloadImage"); // 當前執行緒的名字,可以任意字串 handlerThread.start(); // 必須先開啟執行緒 /** * 該callback運行於子執行緒 */ class ChildCallback implements Handler.Callback { @Override public boolean handleMessage(Message msg) { // 在子執行緒中進行相應的網路請求 // 通知主執行緒去更新UI mUIHandler.sendMessage(msg1); return false; } } Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback()); final HandlerThread downloadAThread = new HandlerThread("downloadAThread"); downloadAThread.start(); Handler downloadAHandler = new Handler(downloadAThread.getLooper()); // downloadAHandler 子執行緒的 Handler downloadAHandler.postDelayed(new Runnable() { @Override public void run() { Toast.makeText(getApplicationContext(), "下載A完成", Toast.LENGTH_SHORT).show(); mainHandler.post(new Runnable() { @Override public void run() { tv_A.setText("A任務已經下載完成"); } }); } }, 1000 * 5);