Handler介紹

Handler是什麼

概述

Android Framework提供的一個基礎組件,用於線程間通訊。主要是子線程UI更細消息傳遞給主線程,從而主線程更新UI。java

Android 主線程的UI,只能主線程更新。 若是多個線程都能更新,勢必要「加鎖」,還不如採用「單線程消息隊列機制」android

主線程內部維護一個循環。沒有消息時候,這個循環是阻塞的。新來消息(或者阻塞timeout)時會喚醒,接着處理新到來消息。git

Java層Handler

整個handler能夠粗略的分爲三個過程數組

  • 構建與輪詢消息
  • 發送消息與喚醒等待
  • 分發處理消息

咱們使用Handler第一感官是發消息,因此從發消息切入。緩存

1.發送消息與喚醒等待

咱們先來看發送消息,這是咱們最簡單的一種發送消息的方式。markdown

//構建一個處理message的handler
val handler = object : Handler() {
     override fun dispatchMessage(msg: Message) {
          Log.d("alvin", "msg.what:${msg.what}")
      }
}
var msg = Message().also { it.what = 1 }
handler.sendMessageDelayed(msg,1000L)
複製代碼

稍稍追蹤下代碼咱們發現,有不少sendMessage方式,都統一到一個方法中即app

//Handler.java
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;//這個分發用的,省略了一下其餘代碼
        return queue.enqueueMessage(msg, uptimeMillis);
}
複製代碼

接着調用到MessageQueue.enqueueMessage(Message msg, long when),代碼在下面, 解釋下,咱們有一個單鏈表message,message有next指向下一個message,以及此message觸發時間when。單鏈表是有順序的,when越小,就越靠前。 根據when,新message插入合適位置,若是剛好插在隊首,則須要喚醒nativeWake。因此歸結下來兩件事情:入隊和喚醒。截屏2021-03-25 下午9.09.52.pngide

//MessageQueue.java 忽略一些代碼了,只關心,咱們此次走到的邏輯
boolean enqueueMessage(Message msg, long when) {
        synchronized (this) {
            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 {
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when)  break;
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
            if (needWake) nativeWake(mPtr);
        }
        return true;
    }
複製代碼

2.構建與輪詢消息

咱們主要到構建一個Handler會調用到MessageQueue.javaoop

val handler = object : Handler()
 -->mLooper = Looper.myLooper();
	-->sThreadLocal.get()
    
ActivityTread.main()
-->Looper.prepareMainLooper()//構建當前線程的Looper
   -->Looper.prepare()
 	  -->new Looper(quitAllowed)
    	 -->mQueue = new MessageQueue(quitAllowed);
			-->mPtr = nativeInit()
-->Looper.loop();//啓動輪詢
複製代碼

那主線程的Looper哪裏來的,早在應用主線程被啓動時,咱們便post

  • 構建當前線程的Looper,Looper.prepareMainLooper()
  • 啓動輪詢,Looper.loop()

2.1.建立並存儲Looper到線程局部變量

//ActivityTread.java
class ActivityTread{
	public static void main(String[] args) {
         Looper.prepareMainLooper();
	}
}

//Looper.java ,省略了部分代碼
class Looper{
    /** *能夠看到sThreadLocal中的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));
    }

    /** * Initialize the current thread as a looper, marking it as an * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
             if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
}  
複製代碼

2.2.啓動輪詢

Looper.loop() 省略部分代碼。就堵塞在這了nativePollOnce(ptr, nextPollTimeoutMillis)。 此處沒有用java中的wait/notify堵塞,而是經過Linux的epoll機制來堵塞,緣由是須要處理 native側 的事件。 沒有消息時堵塞並進入休眠釋放CPU資源,有消息時再喚醒線程。 epoll參考:epoll的本質

class Looper{
    public static void loop() {
        final Looper me = myLooper();
        final MessageQueue queue = me.mQueue;
        for (;;) {
            // 1.阻塞,直到next有返回值。
            Message msg = queue.next(); 
            //2.後面提到的消息分發處理
            msg.target.dispatchMessage(msg);
        }
    }
}

class MessageQueue {
    Message next() {
        /**等於0時,不堵塞,當即返回; *大於0時,最長堵塞等待時間,期間有新消息進來,可能會了當即返回(當即執行); *等於-1時,無消息時,會一直堵塞; */
        int nextPollTimeoutMillis = 0;
        for (;;) {
            //阻塞在此,超時或者被喚醒時,執行接下來的代碼
            nativePollOnce(ptr, nextPollTimeoutMillis);
            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null) {
                    if (now < msg.when) {
                    	//咱們發現拿到的第一個消息,執行時間還沒到,那咱們計算下超時時間,用於下一次的nativePollOnce
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.提取這個消息,並如下一個消息從新做爲消息頭
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
            }
        }
    }
}
複製代碼

3. 分發處理消息

在Looper.loop()中執行了 msg.target.dispatchMessage(msg); msg.target就是咱們的Handler對象。

早在Handler.enqueueMessage(msg,...)時,咱們執行了msg.target = this;

class Handler{
    public void dispatchMessage(@NonNull Message msg) {
        //msg.callback跟蹤下代碼。哦,原來是post(Runnable),時候構建一個帶callback的message
        if (msg.callback != null) {
            handleCallback(msg);//-->message.callback.run();
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //咱們並無給handler設置mCallback,因此執行handleMessage
            handleMessage(msg);
        }
    }
    
    //post(Runnable),時候構建一個帶callback的message
    public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
}
複製代碼

4.Java層總結

截屏2021-03-29 下午8.34.33.png 雖然主流程沒有了問題,但咱們省略了不少代碼,能夠做爲問題來探索這些省略的代碼

  • SyncBarrier 同步屏障是啥有何做用
  • Looper.loop() 能退出麼,若是一直沒有消息的話,阻塞在那,豈不是會anr?
  • Message.obtain(),消息緩存池
  • Handler.removeCallbacksAndMessages()

Native層Handler

消息輪詢機制是經過MessageQueue.nativePollOnce()掛起和MessageQueue.nativeWake喚醒。 MessageQueue經過mPtr變量保存NativeMessageQueue對象,從而使得MessageQueue成爲Java層和Native層的樞紐,既能處理上層消息,也能處理native層消息;下面列舉Java層與Native層的對應圖。 要注意的是Java層和C++層分別實現了一套Handler機制代碼。 image.png

Looper.prepare()

Java層:new Looper()-->new MessageQueue()-->mPtr = MessageQueue.nativeInit() -->native層:android_os_MessageQueue_nativeInit()-->new NativeMessageQueue()-->new Looper(false) 最終走到了Looper.cpp的構造方法

Looper::Looper(bool allowNonCallbacks)
    rebuildEpollLocked();
}
void Looper::rebuildEpollLocked() {
    struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
    eventItem.events = EPOLLIN;//監聽mWakeEventFd有寫入數據。
    eventItem.data.fd = mWakeEventFd.get();
    //建立 eventpoll 對象,返回一個 epfd,即 eventpoll 句柄。
    int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);
}
複製代碼

要說 epoll_ctl()幹嗎用的,我也不太會了, 能夠參考。

Android Handler 中的 epoll 機制

若是這篇文章說不清epoll的本質,那就過來掐死我吧!

Looper.loop()

從Java層分析咱們獲得Looper.loop()-->MessageQueue.nativePollOnce(mPtr,timeout) 進入native層:android_os_MessageQueue_nativePollOnce()-->NativeMessageQueue::pollOnce()               -->Looper::pollOnce()-->Looper::pollInner() 一頓操做進入到了Looper::pollInner()

int Looper::pollInner(int timeoutMillis) {
    //1.timeoutMillis處理,省略...
    //2.開啓等待,監聽mWakeEventFd寫入事件
    	//events 表示回傳處理事件的數組;
    	//maxevents 表示每次能處理的最大事件數;
    	//timeout:等待 IO 的超時時間,-1 表示一直阻塞直到來 IO 被喚醒,大於 0 表示阻塞指定的時間後被喚醒
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    //3.後面等喚醒後執行,先執行native層添加的message,邏輯與java層處理消息相似。
    //4.走回java層,MessageQueue.nativePollOnce(),執行java層的消息提取。
}
複製代碼

Handler.sendMessage()

根據前面Java層分析,進入到MessageQueue.nativeWake() 進入native層:android_os_MessageQueue_nativeWake()-->NativeMessageQueue::wake()-->Looper::wake() 一頓操做進入Looper::wake()

void Looper::wake() {
    uint64_t inc = 1;
    //往mWakeEventFd裏面寫了1,隨後進入到Looper::pollInner(),喚起」epoll_wait「,繼續執行。
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
}
複製代碼
int Looper::pollInner(int timeoutMillis) {
    //1.timeoutMillis處理,省略...
    //2.開啓等待,監聽mWakeEventFd寫入事件,省略...
    int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    //Looper::wake()後執行到這。
    //3.後面等喚醒後執行,先執行native層添加的message,邏輯與java層處理消息相似
     while (mMessageEnvelopes.size() != 0) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
        if (messageEnvelope.uptime <= now) {
           		sp<MessageHandler> handler = messageEnvelope.handler;
                Message message = messageEnvelope.message;
                mMessageEnvelopes.removeAt(0);
                handler->handleMessage(message);
        } else {
            // The last message left at the head of the queue determines the next wakeup time.
            mNextMessageUptime = messageEnvelope.uptime;
            break;
        }
    }
    //4.走回java層,MessageQueue.nativePollOnce(),執行java層的消息提取。
}
複製代碼

Native層總結

與Java層相似,但同時咱們省略了不少代碼,帶着問題繼續看代碼

  • Looper不單單用來處理Handler,還有其餘做用,看看MessageQueue.addOnFileDescriptorEventListener代碼
  • ALooper.cpp是啥

參考

換個姿式,帶着問題看Handler

Android消息機制1-Handler(Java層)

Android Handler 中的 epoll 機制

若是這篇文章說不清epoll的本質,那就過來掐死我吧!

相關文章
相關標籤/搜索