Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Handler消息機制 源碼解讀

Handler消息機制 源碼解讀

編輯:關於Android編程

基本概念

Handler消息機制的作用

大家知道子線程沒有辦法對UI界面上的內容進行操作,如果操作,將拋出異常:CalledFromWrongThreadException,為了讓子線程能間接操作UI界面,Android中引入了Handler消息傳遞機制,通過Handler切換到主線程進行UI操作。

Handler、Looper、MessageQueue、Message的關系是什麼?

Handler用於發送和處理消息。而發出的Message經過一系列的周轉後,最終會傳遞回Handler中,最後更新UI。

Message是在線程之間傳遞的消息,它可以在內部攜帶少量的信息,用於在不同線程之前交換數據。

MessageQueue是消息隊列,用於存放Message。Message在消息隊列中,等待Looper取出。每個線程中只會有一個MessageQueue對象。

Looper是每個線程中的MessageQueue的管家,調用Looper的loop()方法後,就會進入一個無限循環中,每當MessageQueue中存在一個Message,Looper對象就會將其取出,傳遞到Handler中進行處理。每個線程中只會有一個Looper對象。

Handler消息機制疑問

從上面的基本概念中,我們不難會產生疑問,比如:

在Message的周轉過程中,是怎麼切換到主線程的? 如果當前線程中new了多個Handler,它們發送消息後會錯亂嗎?會不會找錯Handler對象。 Handler的post方法和sendMessage有什麼區別? 怎麼保證每個線程中只能存在一個Looper和MessageQueue? 子線程為什麼不能直接new Handler? 主線程為什麼不用手動創建Looper? 主線程中的Looper無限循環,為什麼沒有造成ANR?

下面,我將從源碼角度,對以上疑問進行探索。看完本篇博客,相信你心裡就會有答案了。

初識API

Message

我們先來簡單認識一下Message

    public int what;
    public int arg1; 
    public int arg2;
    public Object obj;

上面幾個的用法,大家都知道,就不用介紹了。我們來看看下面的。

    Handler target;
    Runnable callback;

Message中可以保存Handler對象,也可以保存Runnable,具體用法看完本篇博客就知道了。
還記得Message.obtain用法嗎?

 public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; 
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

因為Message被處理完過後,是不會直接回收的,默認會保存一定數量的Message以供復用。我們可以使用Message.obtain復用,以免創建多余的Message。

MessageQueue

MessageQueue是什麼?我們可以給它理解成一個集合,比如List,我們可以添加消息,也可以讀取消息,移除消息。當然MessageQueue的內部是通過一個單鏈表的數據結構來實現的,理解起來可能有點費勁,我們只需知道有兩個重要的方法enqueueMessage插入消息,next取出消息。

  //======================插入消息=================================
    boolean enqueueMessage(Message msg, long when) {
            //...
            //省略了部分代碼
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                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;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }


    //======================取出消息=================================

Message next() {
             //...
            //省略了部分代碼
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
              //...
            //省略了部分代碼
        }
    }

工作原理

發送消息(將Message提交到MessageQueue)

Handler平時用的都比較多,一般都會直接使用 mHandler.sendMessage進行發送消息,然後重寫Handler的handleMessage進行接收和處理消息。那麼 mHandler.sendMessage 到底做了什麼事呢?

public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg, 0);
    }

public final boolean sendMessageDelayed(Message msg, long delayMillis){
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

我們可以發現,在Handler內部,其實調用的是sendMessageDelayed,然後sendMessageDelayed中又調用了sendMessageAtTime

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

sendMessageAtTime中我們看到了什麼?沒錯MessageQueue,而且MessageQueue不可為空,否則會拋出異常,你可能為疑問,這個MessageQueue是從哪裡來的,不要急,下面馬上就會介紹,在Handler的構造方法那裡就能看到。我們暫且不管MessageQueue是怎麼工作的,我們只需知道,我們當前的任務是將Message塞進MessageQueue中,我們接下來看看enqueueMessage中做了什麼。

 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

我們可以發現,這裡調用了MessageQueue中的queue.enqueueMessage方法將Message插入到隊列中去,到此為止,我們完成了第一步。
我們還能發現這一行msg.target = this;,沒錯,將當前的Handler對象綁定給了Message,這也就能解釋,為什麼Message一番周轉之後,仍然知道該傳遞給哪個Handler對象。

取出消息(將Message從MessageQueue中取出)

Message塞給了MessageQueue後,現在就該輪到Looper登場了。Looper是怎麼從MessageQueue取出中取出Message的呢。

Looper的屬性

先來簡單看一下Looper的三個屬性。

    static final ThreadLocal sThreadLocal = new ThreadLocal();
    private static Looper sMainLooper; 
    final MessageQueue mQueue;

可以看出Looper中會綁定一個對應的MessageQueue,還有一個線程變量sThreadLocal

子線程中為什麼不能直接創建Handler

在介紹之前,我們先來解釋下,為什麼不能直接在子線程中new Handler。
大家都知道在線程中使用Handler如下,但是可能不知道為什麼要這麼寫。

  Looper.prepare();
  Handler handler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
  Looper.loop();

那麼,我們先來看一下Handler構造方法,Handler構造方法有兩種,一種是顯示指定Looper對象的,另一種是不顯示指定Looper的(會默認獲取當前線程的Looper),所有不顯示指定Looper的構造方法都會在內部轉為調用以下構造。

public Handler(Callback callback, boolean async) {
        //...
        //省略了部分代碼
        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 = callback;
        mAsynchronous = async;
    }

我們可以發現只要mLooper為空,就會拋出異常。不為空的話就連同MessageQueue賦值給當前Handler,你可能又想問,Looper的MessageQueue是怎麼來的,莫急,待會會介紹Looper的構造方法。我們先看看Looper.myLooper()方法

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

sThreadLocal是什麼鬼?

 static final ThreadLocal sThreadLocal = new ThreadLocal();

看到這裡終於明白了,原來用了ThreadLocal啊,ThreadLocal是Java用來存儲線程變量的api。也就是說,假如我們在主線程使用sThreadLocal存儲了一個Looper,在子線程也使用了sThreadLocal存儲了一個Looper,他們互不干擾。每個線程取出來的值都是不一樣的。這也就保證了,每一個線程都有一個單獨的Looper。
那麼如何在子線程使用Handler呢?相信大家都有思路了,只要保證sThreadLocal.get()能取到值就行,我們可以在new 之前給當前線程一個Looper對象。api中已經提供了方法,Looper.prepare()如下。

    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));
    }

而且也可以看出,每個線程中只能設置一個Looper對象,否則還是會拋出異常。為什麼要強制只能有一個Looper對象呢?當然是因為Android的單線程模型啊。如果允許多個Looper那麼和在子線程中直接處理沒有任何區別,會導致一系列的並發問題。

sThreadLocal.set(new Looper(quitAllowed))給當前線程綁定一個Looper,我們來看一下Looper的構造。

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

從源碼可以看出,與此同時,也給Looper綁定了一個MessageQueue對象。

Looper取消息

那麼,Looper到底是怎麼從MessageQueue中取出Message的呢。我們來看下 Looper.loop()

    public static void loop() {
        final Looper me = myLooper();//獲取當前線程的Looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue; //獲取Looper對應的消息隊列
         //...
         //省略了部分代碼
        for (;;) {//死循環
            Message msg = queue.next();  //從消息隊列中取出一下個Message
              //...
             //省略了部分代碼
            msg.target.dispatchMessage(msg);//msg.target就是Handler,調用Handler的dispatchMessage方法處理消息

              //...
             //省略了部分代碼
            msg.recycleUnchecked();//回收Message
        }
    }

流程非常清晰,通過一個死循環,不停調用MessageQueuenext()方法,取出Message,然後看到了沒msg.target,前面我們發送消息時,綁定的Handler對象。經歷了一番周轉變換了線程,又交給了Handler對象的dispatchMessage中進行處理消息。

處理消息(Handler調用dispatchMessage處理消息)

在介紹處理消息之前,我們先來認識一下Handler的其他使用方法。我們知道只要重寫Handler的handleMessage方法,然後就可以接收消息了。但是,我如果不想重寫handleMessage呢?有沒有其他方法?當然是有的,請往下看。

Handler的構造參數

前面我也提到了,Handler的構造方法分為兩種,顯示指定Looper與否,其實這兩種都是一樣的,沒有本質上的區別。但是我們忽略了Handler構造方法中的其他參數,現在再來看一遍。

public Handler(Callback callback, boolean async) {
        //...
        //省略了部分代碼
        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 = callback;
        mAsynchronous = async;
    }

有沒有發現一個Callback參數,其實它是一個接口,也是用於處理消息的。

    public interface Callback {
        public boolean handleMessage(Message msg);
    }

使用mHandler.post發送消息。

mHandler.sendMessage這種方法大家都很常用,也很簡單。大家都知道Handler有個post方法,那麼它和sendMessage有什麼區別呢?我們來看一下源碼。

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

從上面可以看出,使用post,仍然會包裝為Message,然後調用sendMessageDelayed進行發送消息。先來看一下到底是怎麼包裝的,如下

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

包裝給了一個callback,就是前面提到的Runable。

Handler處理消息

在介紹完Handler的其他用法後,現在回到Handler的dispatchMessage中。

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

看到沒有,if (msg.callback != null),也就是說,如果是使用post發送的,會調用handleCallback進行處理。

    private static void handleCallback(Message message) {
        message.callback.run();
    }

直接走了Runable的run方法。不會走Handler的handleMessage
然後 if (mCallback != null)判斷那個接口有沒有實現,如果實現了,直接走接口。最後才是調用Handler內部的handleMessage方法。

最後

主線程為什麼不用手動創建Looper?

我們來看一個類ActivityThread,這個類是Android的入口,我們可以找到一個久違的方法public static void main(String[] args)

    public static void main(String[] args) {
       //...
       //省略了部分代碼

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        //...
       //省略了部分代碼
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

從上面可以看出,Android在啟動一個應用時,已經默認給主線程開啟了Looper。

主線程中的Looper無限循環,為什麼沒有造成ANR?

什麼情況下會造成ANR呢?只有當主線程的Handler處理一個消息耗時過長才會ANR。
Looper無限循環,不是一個簡單的死循環,當消息隊列中沒有消息時,就會阻塞休眠,釋放cpu資源。
我們Activity之所以有生命周期,也依賴於Handler,是因為ApplicationThread根據系統ActivityManageService發來的事件,然後發送消息來調度生命周期的。以下是ActivityThread中處理消息相關的部分代碼。

 public void handleMessage(Message msg) {
              //..
            //省略了部分代碼
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;

                case PAUSE_ACTIVITY:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                    handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2,
                            (msg.arg1&2) != 0);
                    maybeSnapshot();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
             //..
             //省略了部分代碼
            }

        }

END

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved