Android面向面试复习----Handler详解

时间:2022-12-03 04:38:53

Handler详解

这篇文章缘起于一道面试题:
Android面试题 请解释下单线程模型中Message、Handler、MessageQueue、Looper之间的关系
虽然能够大致说明白,但是自己对答案也不太满意,翻一翻源码,从源码角度剖析一番。

1. 概述Handler相关对象模型关系

  1. 首先看一下Handler、Looper、MessageQueue、Message的相关类图

    Handler中有两个成员变量:mLooper、mQueue,分别对应的是Looper和MessageQueue的实例。
    Looper中包含一个成员变量mQueue,对应的是MessageQueue的实例。
    在创建Handler的时候会获取当前线程的Looper,并将Looper对应的消息队列也赋值给Handler中的mQueue,用来存储消息。

    Android面向面试复习----Handler详解

  2. Handler的构造方法

    平时我们都是new Handler()创建,最后还是调用的两个参数的构造方法

    public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
    final Class<? extends Handler> klass = getClass();
    if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
    (klass.getModifiers() & Modifier.STATIC) == 0) {
    Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
    klass.getCanonicalName());
    }
    }

    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;
    }
    1. 在构造方法中通过mLooper = Looper.myLooper();创建了Looper。

      1. myLooper方法中是从sThreadLocal中获取的Looper:sThreadLocal.get()
      2. Looper是在什么地方创建的呢?答案是prepare中,所以在子线程中,没有调用Looper.prepare()不能创建Handler,而主线程默认执行了Looper.prepare(),所以不会有这个问题。

        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));
        }
      3. sThreadLocal是跟线程相关的,所以,每个线程可以对应一个Looper及MessageQueue
    2. 通过Looper,获取到了Looper对应的MessageQueue,并赋值给Handler中的MessageQueue

    3. 这样就把Handler和Looper、MessageQueue串联了起来。

2. 从源码角度剖析3个流程

  1. 发送消息

    发送消息有两种方式:sendMessage(Message msg),或者post(Runnable r),最终都是调用到了sendMessageAtTime(Message msg, long uptimeMillis)

         public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
    RuntimeException e = new RuntimeException(
    this + " sendMessageAtTime() called with no mQueue");
    Log.w("Looper", e.getMessage(), e);
    return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
    }

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
    msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
    }

    其中最关键的是queue.enqueueMessage(msg, uptimeMillis)消息队列把Message放到了消息队列中,存储起来。

  2. 分发消息

    Looper调用了loop()方法之后,就会不停的从MessageQueue中读取消息,只要读取到消息,就会进行分发处理。下面我们把loop()中读取消息,分发处理逻辑摘出来。

    for (;;) {
    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);
    }

    final long traceTag = me.mTraceTag;
    if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
    Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
    }
    try {
    msg.target.dispatchMessage(msg);
    } finally {
    if (traceTag != 0) {
    Trace.traceEnd(traceTag);
    }
    }
    ...
    }

    其中:Message msg = queue.next();从消息队列中取出消息
    如果消息不为空,则使用Handler的dispatchMessage方法进行分发处理。

    public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
    handleCallback(msg);
    } else {
    if (mCallback != null) {
    if (mCallback.handleMessage(msg)) {
    return;
    }
    }
    handleMessage(msg);
    }
    }

    这里主要有两个分支:

    • handleCallback(msg)是处理的Runnable任务
    • handleMessage(msg)是处理的普通消息任务
  3. 处理消息

    处理消息就是执行HandleMessage中的逻辑或者Runnable中的逻辑。

    1. 在看Looper.loop()方法逻辑的时候,最后一行被我删掉了,这一行是等消息分发处理完后对msg的回收处理。

      msg.recycleUnchecked();
    2. 源码如下:

      void recycleUnchecked() {
      // Mark the message as in use while it remains in the recycled object pool.
      // Clear out all other details.
      flags = FLAG_IN_USE;
      what = 0;
      arg1 = 0;
      arg2 = 0;
      obj = null;
      replyTo = null;
      sendingUid = -1;
      when = 0;
      target = null;
      callback = null;
      data = null;

      synchronized (sPoolSync) {
      if (sPoolSize < MAX_POOL_SIZE) {
      next = sPool;
      sPool = this;
      sPoolSize++;
      }
      }
      }

3. 文章开头的面试题答案整理

工作流程:

  1. 在单线程中,Looper轮询器被调用prepare()和loop()后,它会不断的从MessageQueue头部读取Message;
  2. 创建Handler时,Handler内部会持有当前线程对应的Looper和MessageQueue的引用。
  3. 如果该线程中有Handler发送消息给MessageQueue,Looper就能够取出该消息,通过Message的target(Handler)的dispathMessage,进行处理(两个分支,一个处理Runnable,一个处理普通Message)。
  4. 处理完成后,Looper又继续进行消息的拉取,如此循环往复。直到调用removeCallbacksAndMessages可以将当前Handler中的所有任务给取消掉。