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的本质,那就过来掐死我吧!

相关文章
相关标签/搜索