Handler實現原理解析

目錄:

Handler

Handler概述:

Handler並不是專門用來更新UI界面的,只不過日常開發中,開發者用來更新UI比較多.

Handler的主要作用是:將一個任務切換到某個指定線程進行執行.

Handler的主要目的是:爲了解決在子線程中無法更新UI界面的矛盾問題


Handler原理圖:

image

主線程中爲什麼可以直接創建Hanlder:

在非主線程中創建Handler的時候需要先調用Looper.prepare();創建Looper對象以及完成消息隊列的初始化,然 後創建Handler對象,最後,調用Looper.loop();開啓消息循環;

而在主線程中,App的入口,ActivityThread.main方法中:

public static void main(String[] args) {
       // 不相干代碼
       ......
       // 1.調用Looper.prepareMainLooper,其實也就是調用的Looper.loop,初始化Looper、MessageQueue等
       Looper.prepareMainLooper();
       // 2.創建ActivityThread的同時,初始化了成員變量Handler mH
       ActivityThread thread = new ActivityThread();
       thread.attach(false);
       // 
       if (sMainThreadHandler == null) {
           // 把創建的Handler mH賦值給sMainThreadHandler
           sMainThreadHandler = thread.getHandler();
       }

       if (false) {
           Looper.myLooper().setMessageLogging(new
                   LogPrinter(Log.DEBUG, "ActivityThread"));
       }
       // 3.調用Looper.loop()方法,開啓死循環,從MessageQueue中不斷取出Message來處理
       Looper.loop();

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

主線程不需要手動調用,是因爲系統在啓動App時,就幫我們調用了


Handler使用的兩種方式:

**一:post方式**
Handler mHandler = new Handler();  
mHandler.post(new Runnable() {  
    @Override  
    public void run() {  }  
}); 

二:Message方式

//主線程中創建Handler
mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                // 處理返回的消息
            }
        };
        
//---------------------------------------------        

//在子線程總調用Handler發送消息
Message msg = Message.obtain();
msg.what = MSG_SUB_TO_MAIN;
msg.obj = "這是一個來自子線程的消息";
// 2.發送消息
mHandler.sendMessage(msg);


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;
        ...
    }
  • 在創建Handler對象的時候,在構造函數中首先會初始化Looper對象並獲取其中的MessageQueue消息隊列對象;
  • 當創建Handler對象的線程中沒有初始化Looper對象的時候就會導致拋出異常

post,Message調用的原理

post方式調用首先會調用getPostMessage(r)方法;
private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

在該方法中,創建一個Message對象,將需要執行的Runable對象設定爲callback

隨後,post方法就和Message方法一樣,都會調用sendMessageDelayed()方法

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

在該方法中對設定的延遲時長進行判斷,隨後都調用了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);
    }
    
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    
  • 在sendMessageAtTime()獲取了Looper中的MessageQueue對象
  • 在enqueueMessage()中將Hnadler對象賦值給了msg.target,並且將傳入的Message對象進行了enqueueMessage入隊操作

Looper

Looper的構造函數

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

在創建的Looper的時候在Looper的構造函數中會首先初始化好一個MessageQueue用來保存需要處理的meassage,所以消息隊列在looper對象進行初始化創建的時候一併創建好了.


Looper的創建

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));
    }
    
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

Looper創建的時候調用prepare方法,每個線程中只允許存在一個Looper,所以在重複創建會導致異常,當首次創建完成後,會將該Looper對象存儲到sThreadLocal中,並且在構造函數中完成隊列MessageQueue的初始化


開啓Looper循環的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;
        ...省略部分代碼
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
        ...省略部分代碼
        try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ...省略部分代碼
            msg.recycleUnchecked();
        }
    }
  • 在Looper中執行循環的是loop方法,在方法中是一個無限循環,其中就調用到了MessageQueue中的next方法,而next方法也是一個無效循環的方法,在沒有消息的時候就處於阻塞狀態,在有消息的時候就對消息進行返回並將該條消息從消息隊列中移除;所以綜合來說,Looper中的無線循環通過MessageQueue的next方法獲取到了消息.

  • 唯一退出循環的判斷條件是next爲null,在什麼情況下next會返回null呢?當調用Looper的quit方法時,會調用MessageQueue中的quit或者quitSaffely,兩個退出方法二者的區別是一個立即退出,一個是將當前隊列狀態修改爲退出狀態,等任務執行完成之後退出,當消息隊列被標記爲退出狀態時,next會返回null;

  • 在獲取消息隊列中的message對象後,會通過msg.target獲取到Handler對象,去調用Handler中的dispatchMessage(msg);方法

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

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

在dispatchMessage方法中,首先會判斷Runnable對象是否爲空,假如我通過post方法封裝了需要執行的Runnable,那麼在此處就會通過handleCallback()進行執行;而不論有沒有,最終都會再執行Handler中的handleMessage()方法

MessgaeQueue工作原理:

MeassgaeQueue翻譯的爲消息隊列,但它實質上並不是一個消息隊列,它的內部是一個單鏈表的數據結構用來存儲數據,但其並不對數據進行處理.

MessgaeQueue主要用到的有兩個方法enqueueMessage和next,
enqueueMessage主要用來存儲數據,想單鏈表中插入一條數據,next則是向鏈表中取出一條數據,並從隊列中刪除,需要說明的一點是,在next內部是一個無線循環,當沒有消息的時候就處於阻塞狀態,在有消息的情況下就會進行返回並將該條消息從隊列中移除.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章