Handler消息机制在Android有着非常重要的地位,与Binder的IPC机制一样,贯穿了整个Android系统。
说到Handler,绝对是高频面试题,网上太多Blog里分析Handler机制,但不乏很多都是搬运别人文章直接转载发出来,将别人的理解照搬成自己的理解,所以我自己就想着写一篇关于Handler消息机制的文章,全文都是基于自己的理解,如有问题,尽情提出,大家可以抱着学习态度阅读此文。下面我们就带着几个问题就理解Handler消息机制。
- ①Handler是什么?。
- ②消息是怎么发送的,它的整个流程是什么?
- ③Message是什么?它里面都有什么?
- ④MessageQueue是什么?它和Message的关系是怎样的?
- ⑤Looper是什么?它的初始化时机是什么时候?它是怎样拿到Message的?
- ⑥ThreadLocal是什么?HandlerThread是什么?
一、 Handler是什么
它是Android定义的一套线程间切换,特别是实现主线程和子线程切换的通讯机制,因为Android是强烈规定更新UI的线程为子线程,其他线程为子线程(分线程),耗时操作不建议在主线程上执行,这样会导致手机ANR(Application Not Responding),早期的ANR手机直接崩溃。
二、消息是怎么发送的,它的整个流程是什么?
- 当一个消息被发送,调用Handler的sendMessage方法发送消息:
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
- 消息进入消息队列:
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
-
消息通过消息时延when的大小排队:
boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } synchronized (this) { if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); 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 { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; //消息通过when排队 if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
-
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."); } if (me.mInLoop) { Slog.w(TAG, "Loop again would have the queued messages be executed" + " before this one completed."); } me.mInLoop = true; 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(); // Allow overriding a threshold with a system prop. e.g. // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start' final int thresholdOverride = SystemProperties.getInt("log.looper." + Process.myUid() + "." + Thread.currentThread().getName() + ".slow", 0); boolean slowDeliveryDetected = false; for (;;) { //next()这是取出消息的地方 Message msg = queue.next(); // might block if (msg == null) { // 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 final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } // Make sure the observer won't change while processing a transaction. final Observer observer = sObserver; final long traceTag = me.mTraceTag; long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs; if (thresholdOverride > 0) { slowDispatchThresholdMs = thresholdOverride; slowDeliveryThresholdMs = thresholdOverride; } final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0); final boolean logSlowDispatch = (slowDispatchThresholdMs > 0); final boolean needStartTime = logSlowDelivery || logSlowDispatch; final boolean needEndTime = logSlowDispatch; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0; final long dispatchEnd; Object token = null; if (observer != null) { token = observer.messageDispatchStarting(); } long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid); try { msg.target.dispatchMessage(msg); if (observer != null) { observer.messageDispatched(token, msg); } dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } catch (Exception exception) { if (observer != null) { observer.dispatchingThrewException(token, msg, exception); } throw exception; } finally { ThreadLocalWorkSource.restore(origWorkSource); if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (logSlowDelivery) { if (slowDeliveryDetected) { if ((dispatchStart - msg.when) <= 10) { Slog.w(TAG, "Drained"); slowDeliveryDetected = false; } } else { if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery", msg)) { // Once we write a slow delivery log, suppress until the queue drains. slowDeliveryDetected = true; } } } if (logSlowDispatch) { showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg); } if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); } }
-
在Looper 的loop()中取出消息,再通过msg.target.dispatchMessage(msg)分发消息:
public void dispatchMessage(@NonNull Message msg) { if (msg.callback != null) { //runnable取出消息 handleCallback(msg); } else { if (mCallback != null) { //Callback 取出消息 if (mCallback.handleMessage(msg)) { return; } } //直接取出消息 handleMessage(msg); } }
三、Message是什么?它里面都有什么?
- Message说同属一点就是一种数据结构,它是一条单向链表,链表的每个节点就是Message,而Message节点中包含了what(消息标志)、arg1、arg2(Int类那基本数据类型)、obj(Object数据类型)、next(指向下一个Message的指针)、sPool(Message复用池)、target(发消息的那个Handler)、callback(Runnable),Message主要是携带消息标识符和数据的作用,为了节省资源,在Message中使用了消息复用池,当使用完的消息被回收,每次取消息时就不需要重新创建一个Message对象了,直接从消息池中取(调用obtain方法):
public static Message obtain() {
synchronized (sPoolSync) {
//消息复用池中取消息
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
四、MessageQueue是什么?它和Message的关系是怎样的?
- MessageQueue是一个消息队列,类似于我们日常排队,这个队伍就是消息队列MessageQueue,而每个人就相当于Message,它也是一种数据结构,它的实现就是一种先进先出的单向链表结构,每个Message进入前要根据延时when来排队,当loop的prepare参数为false消息队列会一直工作。
五、Looper是什么?它的初始化时机是什么时候?它是怎样拿到Message的?
- Looper是一个取消息的轮询器,它是在ActivityThread main入口方法中被初始化的,此时的Looper就一直在运转,通过loop()轮询,通过myLooper()获取信息:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
六、ThreadLocal是什么?HandlerThread是什么?
前面提到Looper是在Activity中被初始化的,通过Looper.prepareMainLooper()启动Looper,此时调用prepare(false)初始化Looper进而驱动MessageQueue执行。
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 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();
}
//nativeInit是Java Native方法,mPtr这个变量是获取MessageQueue中下一个消息的关键
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
- ThreadLocal是一个本地线程副本变量工具类,主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰。ThreadLocal本身不储存值 主要提供自身引用 和 操作ThreadLocalMap 属性值得方法,使用ThreadLocal会通过ThreadLocal的引用定位到到堆中Thread的类ThreadLocalMap里散列表里的值 从而达到线程私有,它里面维护一个ThreadLoacalMap内部类,ThreadLocal真正核心是ThreadLoacalMap这个类,初始容量为16,加载因子为当前容量大小的2/3,对应的节点是WeakRefrence< ThreadLocal >,保证ThreadLocal 不会泄漏,方便GC。