fwrite

fwrite

好好生活
twitter
github
email

Handler機制

概述#

說明
Handler真正處理事件的地方
Looper循環檢查是否有要傳輸的事件,從 MessageQueue 中獲取事件,並交給 Handler 處理(假如列隊為空就進入休眠)
MessageQueue儲存需要做的事情,一般來說只允許保存相同類型的 Object、Message 除存的數據結構隊列
Message需要做的事件

一個 Thread 對應一個 Looper
一個 Looper 對應一個 MessageQueue
一個 MessageQueue 內有多個 Message
每個 Message 中最多對應一個 Handler 處理事件
Thread & Handler 是一對多的關係

Handler#

接受處理 Message
將 Message 存入 MessageQueue 中

// Handler.java

public class Handler {
    ...

    final Looper mLooper;
    final MessageQueue mQueue;
    final Callback mCallback;

    ...

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

    // 通常用來給使用者 Override 處理事件
    public void handleMessage(@NonNull Message msg) {
    }

    // 分化,有三種方式
    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
}

在上面分發事件有三種方法,並且 這三種分發方式有先後順序,只要前面被處理,就不會使用後面的方式處理
Message 本身的 callback
實現 Callback 接口
Override Handler#handleMessage Function
透過 Handler 把信息入隊到 MessageQueue,方式有 Post & Send 兩種系列,並且可以控制發送的時間
Post 系列:把零散信息轉為 Message 再用 Send 傳出

函數功能
boolean post(Runnable r)直接發出信息
boolean postAtTime(Runnable, long updatetimeMillis)在固定(規定)時間再發出該條信息
Send 系列:參數直接是 Message
函數功能
boolean sendEmptyMessage(int what)直接發送信息
boolean sendMessageArFrontOfQueue(Message msg)把消息發送到信息隊列最前面
boolean sendMessageAtTime(int what, long updatetimeMillis)在固定(規定)時間再發出該條信息
boolean sendMessageDelayed(Message msg, long delayMillis)延遲幾毫秒後發送該信息

Post 實現方式

// Handler.java

public class Handler {

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

    // 包裝 callback 再傳出
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();        // 取得 Message 資源,避免不斷創建,可以復用資源
        m.callback = r;        // 直接設定 Runnable 回調函數
        return m;
    }

    // 最後透過計算 當前時間 + 延長實現,之後就呼叫 sendMessageAtTime 函數
    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    // 在規定時間發送信息
    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            // 若是該 Thread 並沒有規定 MessageQueue 則會拋出錯誤
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

}

發送信息方法#

名稱參數:回傳解釋
postRunnable : boolean接收一個 Runnable 對象作為 Message
postAtTimeRunnable, long : boolean接收一個 Runnable 對象作為 Message,並在等待的規定時間發送 Message
sendEmptyMessageint : boolean發送一個使用者規定的 int 信息,在由使用者自己處理
sendMessageAtTimeMessge, int : boolean發送一個使用者規定的 int 信息,在由使用者自己處理
sendMessageDelayedMessge, long : boolean在等待的規定時間後 (延遲) 發送 Message

Post & Send#

// Send ...
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
    
// Post ...
    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
    
// 最終都會 sendMessageAtTime
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {    // 1. 
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

處理 Message#

從 dispatchMessage 可看出它預設處理 Message 的順序
msg.callback (Runnable)
mCallback (interface)
handleMessage (method)

public void handleMessage(Message msg) {
     // 使用者覆寫
 }
  
 /**
  * Handle system messages here.
  */
 public void dispatchMessage(Message msg) {
     // callback 優先級最高
     if (msg.callback != null) {		// msg.callback is Runnable
         handleCallback(msg);
     } else {
         if (mCallback != null) {
             if (mCallback.handleMessage(msg)) {
                 return;
             }
         }
         handleMessage(msg);
     }
 }

MessageQueue & Message#

如其名,它是一個隊列 FIFO,它透過 本地方法 (Native) 內存指針創建 Queue

private native static long nativeInit();
    
MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

Queue 方法#

名稱參數:回傳解釋
enqueueMessageMessage, long : boolean在指定時間內壓入隊列中
nextvoid : Message元素拉出隊列
removeMessagesHandler, int, Object : void移除特定元素
removeMessagesHandler, Runnable, Object : void移除特定元素

MessageQueue#

MessageQueue 因為是 Queue 所以有所謂的 FIFO, 所以有隊列的功能

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}

private native static int nativeInit();

MessageQueue 創建#

連接於 Looper 的建構函數,當 Looper 創建時 MessageQueue 就一同創建

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

Looper 透過 MessageQueue 取得 Message#

// MessageQueue.java

    Message next() {

        // 若已經被 depose mPtr 就會被設定為 0
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration

        // 下一個信息的時間
        int nextPollTimeoutMillis = 0;

        // 進入無限循環,也就是說 next 會導致 block
        for (;;) {
            ... 省略部分

            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) {    // target 就是 Handler

                    // 找到一個可發送的 Message
                    do {
                        prevMsg = msg;
                        msg = msg.next;    // 切換到下一個 Message
                    } while (msg != null && !msg.isAsynchronous());
                }

                // 再次判斷 Message
                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 {
                        // 時間到,並且有 Message
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;    // 上一個 Message 就是當前 Message
                        } else {
                            mMessages = msg.next;    //切換到下一個要用的 Message
                        }

                        // 斷開連接 !! 好讓 GC 侦測到後回收 !!! (可達性分析)
                        msg.next = null;

                        ... debug 訊息

                        msg.markInUse();    // 標記該 Message 已經被使用過
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {        // 檢查是否退出
                    dispose();
                    return null;
                }

                ... 省略 IdleHandler
            }

            ... 省略部分
        }

IdleHandler#

IdleHandler 是 MessageQueue 內的一個 interface,它可以在 MessageQueue 沒有消息時讓使用者知道,並決定如何處理

// MessageQueue.java

    public static interface IdleHandler {
        // 返回true,會讓你設定的接口繼續存在
		// 返回false則會移除你設定的接口
        boolean queueIdle();
    }

同樣分析 next 方法,可以看到一個 MessageQueue 最多設定 4 個 IdleHandler

// MessageQueue.java

    Message next() {

        // 若已經被depose mPtr就會被設定為0
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration

        // 下一個信息的時間
        int nextPollTimeoutMillis = 0;

        // 進入無限循環,也就是說next會導致block
        for (;;) {
            ... 省略部分

            synchronized (this) {

                ... 省略部分

                // 檢查是否有設定IdleHandler
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();


                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                // 最多設定4個Idle Handler
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            
            // 循環
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    // 當沒有信息時通知接口
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                // 返回 false 就移除
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            ... 省略部分
        }
    }

Looper#

它是組成消息的重要關鍵(Handler、Message、MessageQueue),它的角色是驅動

名稱參數:回傳解釋
myLoopervoid : Looper以當前線程為主,取得當前 Thread 的 Looper
getMainLoopervoid : Looper取得主線程的 Looper

Looper - ThreadLocal#

最後 Looper 則是讓整個 Hanler 機制循的動力,有 Looper 的推動 Handler 才能正常接收、發送信息
Looper 中包含了一個 MessageQueue(在構造函數中建構的)

// ava/android/os/Looper.java

public final class Looper {

    // 私有函數
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

}

當你每創建一個線程就必須為這個線程準備 Looper,否則在 Handler 發送消息時就會 crush(上面有說到會檢查),創建主要有以下步驟
Looper 準備工作(prepare 函數)
創建 Handler 發送&接收消息
Looper 開始運作(loop 函數)

class LooperThread extends Thread {

    private Handler handler;

    public void run() {
        // 1
        Looper.prepare();

        // 2.
        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                //... 處理 Message
            }

        }

        // 3.
        Looper.loop();
    }

}

ThreadLocal 這裡大概提及,ThreadLocal 也就是線程隔離,使用線程 Thread 作為 Key 儲存,而 Value 就是你要拷貝到每個線程的數據(每個子線程都持有一個數據)

//android/os/Looper.java

public final class Looper {

    // 拷貝線程的數據是Looper
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null)     // 一個線程只能prepare一次
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 為當前Thread設定Looper
        sThreadLocal.set(new Looper(quitAllowed));
    }
}

當你建立一個 Handler 時,在 Handler construct 就會透過當前的線程取得 Looper

// Handler

    public Handler(@Nullable Callback callback, boolean async) {
        ...
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }


// -----------------------------------------------------------------
// Looper 

    public static @Nullable Looper myLooper() {
        // sThreadLocal是靜態的,會透過當前Thread作為Key取得Looper
        return sThreadLocal.get();
    }

Looper & Handler#

Looper 跟 Handler 的關係可以從 new Handler()這個構造函數中看出,它是透過 myLooper 方法,透過目前的線程在 ThreadLocal 中取得該當前線程所擁有的 Looper

// Handler construct
    public Handler() {
        this(null, false);
    }
    
    public Handler(Callback callback, boolean async) {
        ... 忽略

        // 取得當前線程的 Loop
        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;
    }
    
//Looper...
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

Looper & UI Thread#

Android 中 Activity 的主線程是 ActivityThread’s main ()

public static void main(String[] args) {
        ...

        Looper.prepareMainLooper(); //"1. "

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

        if (sMainThreadHandler == null) {    
            //"2. "
            sMainThreadHandler = thread.getHandler();
        }

        ...

        // End of event ActivityThreadMain.
        ...
        
        Looper.loop();

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

主線程的 Looper 是不可以關閉的,並且使用類同步

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

// 屬性...
    private static Looper sMainLooper;  // guarded by Looper.class

Looper#loop,首先取得目前線程所擁有的 Looper,再取出 Looper 內部有的 MessageQueue,最後無限循環整個消息對列

// Looper.java

public static void loop() {
        final Looper me = myLooper();

        ...

        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 {
            // 解決是誰再呼叫dispatchMessage,target為MessageQueue中的Handler
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ...

            msg.recycleUnchecked();
        }
    }

ThreadLocal#

ThreadLocal 的功能主要是做到線程隔離(用線程隔離數據),簡單來說可以把 ThreadLocal 當作一個 Map(但實際來說它並不是 Map),它的 Key 是 Thread,Value 是一個泛型

// 前面Looper.prepare()內部就有使用到set()方法
    public void set(T value) {
        Thread t = Thread.currentThread();    //"1. "
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }


// 前面Handler()構造函數內部就有使用到get()方法
public T get() {
        Thread t = Thread.currentThread();    //"2. "
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

它使用目前呼叫它的 Thread.currentThread 作為 Key 存入,Value 是泛型
依照當前線程(currentThread),取的 Value

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。