1. 程式人生 > >android Handler機制原理 4個組成部分原始碼解析

android Handler機制原理 4個組成部分原始碼解析

在android開發中,經常會在子執行緒中進行一些操作,當操作完畢後會通過handler傳送一些資料給主執行緒,通知主執行緒做相應的操作。
探索其背後的原理:子執行緒 handler 主執行緒 其實構成了執行緒模型中的經典問題 生產者消費者模型。
生產者消費者模型:生產者和消費者在同一時間段內共用同一個儲存空間,生產者往儲存空間中新增資料,消費者從儲存空間中取走資料

這裡寫圖片描述

好處:
- 保證資料生產消費的順序(通過MessageQueue,先進先出)
- 不管是生產者(子執行緒)還是消費者(主執行緒)都只依賴緩衝區(handler),生產者消費者之間不會相互持有,使他們之間沒有任何耦合

原始碼分析

  • Handler
    • Handler機制的相關類
    • 建立Looper
      • 建立MessageQueue以及Looper與當前執行緒的繫結
    • Looper.loop()
    • 建立Handler
    • 建立Message
    • Message和Handler的繫結
    • Handler傳送訊息
    • Handler處理訊息

Handler機制的相關類

Hanlder:傳送和接收訊息
Looper:用於輪詢訊息佇列,一個執行緒只能有一個Looper
Message:
MessageQueue:

建立Looper

建立Looper的方法是呼叫Looper.prepare() 方法

在ActivityThread中的main方法中為我們prepare了

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        //其他程式碼省略...
        Looper.prepareMainLooper(); //初始化Looper以及MessageQueue

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if
(sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); //開始輪循操作 throw new RuntimeException("Main thread loop unexpectedly exited"); }

Looper.prepareMainLooper();

 public static void prepareMainLooper() {
        prepare(false);//訊息佇列不可以quit
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

prepare有兩個過載的方法,主要看 prepare(boolean quitAllowed) quitAllowed的作用是在建立MessageQueue時標識訊息佇列是否可以銷燬, 主執行緒不可被銷燬 下面有介紹

  public static void prepare() {
        prepare(true);//訊息佇列可以quit
    }
    //quitAllowed 主要
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {//不為空表示當前執行緒已經建立了Looper
            throw new RuntimeException("Only one Looper may be created per thread");
            //每個執行緒只能建立一個Looper
        }
        sThreadLocal.set(new Looper(quitAllowed));//建立Looper並設定給sThreadLocal,這樣get的時候就不會為null了
    }

建立MessageQueue以及Looper與當前執行緒的繫結

   private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);//建立了MessageQueue
        mThread = Thread.currentThread(); //當前執行緒的繫結
   }

MessageQueue的構造方法

 MessageQueue(boolean quitAllowed) {
 //mQuitAllowed決定佇列是否可以銷燬 主執行緒的佇列不可以被銷燬需要傳入false, 在MessageQueue的quit()方法就不貼原始碼了
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

Looper.loop()

同時是在main方法中 Looper.prepareMainLooper() 後Looper.loop(); 開始輪詢

public static void loop() {
        final Looper me = myLooper();//裡面呼叫了sThreadLocal.get()獲得剛才建立的Looper物件
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }//如果Looper為空則會丟擲異常
        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 (;;) {
            //這是一個死迴圈,從訊息佇列不斷的取訊息
            Message msg = queue.next(); // might block
            if (msg == null) {
                //由於剛建立MessageQueue就開始輪詢,佇列裡是沒有訊息的,等到Handler sendMessage enqueueMessage後
                //佇列裡才有訊息
                // 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);
            }

            msg.target.dispatchMessage(msg);//msg.target就是繫結的Handler,詳見後面Message的部分,Handler開始
            //後面程式碼省略.....

            msg.recycleUnchecked();
        }
    }

建立Handler

最常見的建立handler

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

在內部呼叫 this(null, false);

public Handler(Callback callback, boolean async) {
        //前面省略
        mLooper = Looper.myLooper();//獲取Looper,**注意不是建立Looper**!
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;//建立訊息佇列MessageQueue
        mCallback = callback; //初始化了回撥介面
        mAsynchronous = async;
    }

Looper.myLooper();

    //這是Handler中定義的ThreadLocal  ThreadLocal主要解多執行緒併發的問題
  // sThreadLocal.get() will return null unless you've called prepare().
   static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

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

sThreadLocal.get() will return null unless you’ve called prepare(). 這句話告訴我們get可能返回null 除非先呼叫prepare()方法建立Looper。在前面已經介紹了

建立Message

可以直接new Message 但是有更好的方式 Message.obtain。因為可以檢查是否有可以複用的Message,用過複用避免過多的建立、銷燬Message物件達到優化記憶體和效能的目地

public static Message obtain(Handler h) {
        Message m = obtain();//呼叫過載的obtain方法
        m.target = h;//並繫結的建立Message物件的handler

        return m;
    }

public static Message obtain() {
        synchronized (sPoolSync) {//sPoolSync是一個Object物件,用來同步保證執行緒安全
            if (sPool != null) {//sPool是就是handler dispatchMessage 後 通過recycleUnchecked 回收用以複用的Message 
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

Message和Handler的繫結

建立Message的時候可以通過 Message.obtain(Handler h) 這個構造方法繫結。當然可以在 在Handler 中的 enqueueMessage()也綁定了,所有傳送Message的方法都會呼叫此方法入隊,所以在建立Message的時候是可以不繫結的

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

Handler傳送訊息

Handler傳送訊息的過載方法很多,但是主要只有2種。
sendMessage(Message)
sendMessage方法通過一系列過載方法的呼叫,sendMessage呼叫sendMessageDelayed,繼續呼叫sendMessageAtTime,繼續呼叫enqueueMessage,繼續呼叫messageQueue的enqueueMessage方法,將訊息儲存在了訊息佇列中,而最終由Looper取出,交給Handler的dispatchMessage進行處理

我們可以看到在dispatchMessage方法中,message中callback是一個Runnable物件,如果callback不為空,則直接呼叫callback的run方法,否則判斷mCallback是否為空,mCallback在Handler構造方法中初始化,在主執行緒通直接通過無參的構造方法new出來的為null,所以會直接執行後面的handleMessage()方法。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {//callback在message的構造方法中初始化或者使用handler.post(Runnable)時候才不為空
        handleCallback(msg);
    } else {
        if (mCallback != null) {//mCallback是一個Callback物件,通過無參的構造方法創建出來的handler,該屬性為null,此段不執行
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);//最終執行handleMessage方法
    }
}

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

Handler處理訊息

在handleMessage(Message)方法中,我們可以拿到message物件,根據不同的需求進行處理,整個Handler機制的流程就結束了。