Handler和Message以及Looper之間的三角關係

說到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 千里草

相關文章
相關標籤/搜索