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。