說到Handler想必你們都常常用到,在非UI線程更新UI那但是利器,用起來也很是容易上手android
從使用上來講,咱們只須要關注sendMessage和handleMessage便可多線程
因此咱們先從Handler和Message來講起,先看一小段代碼ide
public static final int UPDATE_TEXT_VIEW = 0; public TextView mResultTextView = null; // new 一個 Handler 對象, 之內部類的方式重寫 handleMessage 這個函數 public Handler mMyHandler = new Handler() { // ③ 處理消息 public void handleMessage(android.os.Message msg) { switch (msg.what) { case UPDATE_TEXT_VIEW: /* 這裏可更新 ui */ mResultTextView.setText(String.valueOf(msg.arg1)); break; default: /* do nothing */ break; } }; }; // ================================================================== // 函數名: calc // 日期: 2015-08-30 // 功能: 計算入參並顯示在 UI 上,而後後續以每秒 +1 在 UI 上更新 // 輸入參數: int a // int b // 返回值: 無 // 修改記錄:千里草新增函數 // ================================================================== public void calc(int a, int b) { int c = a + b; final Message msg = new Message(); msg.what = UPDATE_TEXT_VIEW; msg.arg1 = c; // ① 發送消息 mMyHandler.sendMessage(msg); // 這裏更新 UI 線程的TextView OK // mResultTextView.setText(String.valueOf(msg.arg1)); // 啓動一個新的線程來每秒刷新 TextView new Thread() { public void run() { while (true) { msg.arg1++; // ② 發送消息 mMyHandler.sendMessage(msg); // mResultTextView.setText(String.valueOf(msg.arg1)); 這裏會報錯 try { sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; }.start(); }
代碼中有①,②兩處發送消息,. 分別是從UI線程和從子線程發送的消息, 在③處兩個消息均可以被準確的接收到.函數
在接受消息函數handleMessage 裏咱們能夠作任何想作的事情,包括更新UI…oop
從使用來講,你們根據這個例子,想必已經能夠簡單使用Handler來處理多線程之間的交互了…post
此處須要注意的是在使用Handler時,必須須要重寫handleMessage 才能達到咱們想達到的目的...(從邏輯上來講,沒有接受消息的地方,發送的消息有何意義呢,是吧….從原理上..咱們接下來會談到……)ui
談到Handler,咱們還能夠用它執行一個Runnable多線程…例以下面的代碼..this
mMyHandler.post(new Runnable() { @Override public void run() { // 這裏須要注意的是,calc 是在子線程裏被調用,因此calc 就沒法操做 UI 了. calc(1, 2); } });
Handler可真神奇,既能夠發消息也能夠執行某個任務線程.spa
爲了更深層次的瞭解它,只能去看看它的源碼了.let’s go!線程
先看看是如何post一個Runnable線程的
public final boolean post(Runnable r) { //這裏其實是發送了一個Message return sendMessageDelayed(getPostMessage(r), 0); } //組裝一個只帶Runnable的Message private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; } //發送消息 public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }
哎喲喂,這Handler小丫頭片子,還僞裝兩幅面孔呢, 經查看源碼,發現它其實是發送了一個Message消息,然而這個消息僅僅攜帶了一個Runnable對象.
如今來看只須要分析Handler和Message了..咱們來看看最終Handler是如何發送消息的..咱們繼續看源碼,非得把他們扒個精光不可
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { boolean sent = false; //消息隊列,這裏存儲Handler發送的消息 MessageQueue queue = mQueue; if (queue != null) { //Message與Handler綁定 msg.target = this; //消息進隊列 sent = queue.enqueueMessage(msg, uptimeMillis); } else { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); } return sent; }
從消息最終被髮出的函數來看,這裏只作了兩個事情:
1.Message與Handler綁定
2.將Message放進消息隊列裏
消息進隊列以後,Handler發送消息的任務算是完美的完成了,接下來咱們該介紹介紹Looper了,若是沒有Looper,Handler和Message之間的愛情能夠說是不完整的.
咱們先來看看一個Looper是如何使用的吧
class LooperThread extends Thread { public Handler mHandler; public void run() { // 爲當前線程準備一個 Looper Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // 處理消息 } }; // Looper 開始工做 Looper.loop(); } }
//繼續看源碼,prepare執行以後sThreadLocal綁定一個Looper對象 //sThreadLocal 能且只能綁定一個Looper對象 public static void prepare() { prepare(true); } 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)); } /** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ 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; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); //無條件的for循環,來遍歷Messagequeue for (;;) { //獲取下一條消息 Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } //Message綁定的Handler來開始處理消息.. 下面貼上dispatchMessage,函數,你們繼續往下看 msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycle(); } } /** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. */ public interface Callback { public boolean handleMessage(Message msg); } /** * Subclasses must implement this to receive messages. * 子類必須完成handleMessage這個函數來接收消息 */ public void handleMessage(Message msg) { } /** * Handle system messages here. */ public void dispatchMessage(Message msg) { //收到消息後,先處理Runnable 對象callback. if (msg.callback != null) { handleCallback(msg); } else { //若是這裏有回調接口,那麼就直接調用該接口,再也不繼續調用Handler的handleMessage函數來處理消息 if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
從上述加了中文註釋的代碼中能看出,Handler發送的Message經過Looper分發給了各自的Handler.
因此上面說的在使用Handler時,必須須要重寫handleMessage 才能達到咱們想達到的目的...也不徹底正確,
咱們也能夠實現一個CallBack接口來處理消息,用Google的原話是
/** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. */ public interface Callback { public boolean handleMessage(Message msg); }
再貼最後一段代碼來不折不扣說明Handler Looper之間的關係, 那就是Handler的 構造函數
/** * Default constructor associates this handler with the queue for the * current thread. * * If there isn't one, this handler won't be able to receive messages. */ public Handler() { 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()); } } //綁定當前線程的Looper 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 = null; }
一句話總結來講,Handler初始化時綁定了線程的MessageQueue,當前線程的Looper來依次分發MessageQueue裏的消息.
MessageQueue的消息會根據Message的target綁定的Handler被Looper分發到各自的Handler裏去處理.
例如Activity裏有兩個HandlerA和HandlerB, HandlerA發送的消息絕對不可能被HandlerB處理..
先暫時寫到這裏吧,後續補上流程圖和類圖
上述只是我的的很片面的理解,但願你們補充和指出不足的地方…
2015年9月4日 04:10:05 千里草