Android的消息机制 - Handler

305 阅读4分钟

本文将通过阅读源码的方式来学习Handler,并且在面试中Handler也是经常被问到的问题。

阅读源码的方式

根据阅读Handler源码的经验,自我总结了几点阅读源码的方案

  1. what?(要看的是什么)
  2. object(这件事涉及了多少个类)
  3. begin!(从哪里开始,切入点在哪)
  4. relation(这些对象之间存在什么联系)
  5. How to do it(这些对象之间怎么做到的)
  6. Why do you do this(为什么这么做)

要看的是什么

什么是Handler?

Handler就是Android消息机制的上层接口。

Handler的作用

  1. 与其他线程协同工作,接收其他线程的消息并通过接收到的消息更新主UI线程的内容。
  2. 在未来的某个时间点去执行一些事情。

Handler的工作原理

Handler的基本用法

Handler handler = new Handler(){
  @Override
  public void handleMessage(final Message msg) {
    //这里接受并处理消息
  }
};
//发送消息
//send传入Messgae,post传入一个Runnable
handler.sendMessage(message);
handler.post(runnable);

这件事涉及了多少个对象

Handler的源码中涉及了以下几个主要的类

  • Handler:负责发送并处理消息
  • Looper:负责关联线程以及消息的分发(从MessageQueue中取出Message,分发给Handler)
  • MessageQueue:单链表数据结构,用于存储和管理Message
  • Message:消息的实体类,链表结构
  • ThreadLocal:是一个线程内部的数据存储类,主要用于存储当前线程的Looper

从哪里开始,切入点在哪,这些对象之间存在什么联系,这些对象之间怎么做到的

第一个切入点

我们选在Handler的构造方法 Handler所有的构造方法最后都会走到public Handler(Callback callback, boolean async) {} 代码如下:

 public Handler(Callback callback, boolean async) {
        //获取当前线程的Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
        //如果没有则会抛出异常
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        //获取Looper持有的MessageQueue
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

从上面代码可以看出在创建Handler的时候,首先会去检查当前线程的Looper是否存在,不存在会抛出异常。也就可以看出来在创建Handler之前一定要创建Looper。二者一一对应。但是我们在用的时候也没有创建Looper,但是还可以使用。这是因为在主线程中,系统已经为我们创建好了Looper。 如果在子线程中创建Handler,代码如下:

class MyLooperThread extends Thread {
    public Handler mHandler;
    public void run() {
        Looper.prepare();
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                
            }
        };
        Looper.loop();
    }
}

接下来我们分析Looper.prepare()是如何创建Looper的。

public static void prepare() {
     prepare(true);
}

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));
}

上述代码中,创建了Looper对象,并且在ThreadLocal中存储了当前线程的Looper。 注意到,创建Looper的中传了一个参数,该参数默认为true。 它的作用是创建MessageQueue的时候向MessageQueue中传递参数,用来控制主线程的Looper是不可以退出的。代码如下:

if (!mQuitAllowed) {
        throw new IllegalStateException("Main thread not allowed to quit.");
}

Looper.loop()稍后和Message的分发与处理一起分析。

第二个切入点

sendMessage(Message msg)作为第二个切入点。

调用顺序是

sendMessage(Message msg)->
    sendMessageDelayed(Message msg, long delayMillis)->
        sendMessageAtTime(Message msg, long uptimeMillis)->
            enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)->
                enqueueMessage(Message msg, long when)

Handler为我们提供了很多方法 post(Runnable)postAtTime(java.lang.Runnable, long)postDelayed(Runnable, Object, long)sendEmptyMessage(int)sendMessage(Message)sendMessageAtTime(Message, long),和 sendMessageDelayed(Message, long)最后的调用都会走到MessageQueue的enqueueMessage(Message msg, long when)

接下来分析enqueueMessage(Message msg, long when)

代码如下:

    //when = SystemClock.uptimeMillis() + delayMillis
    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            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;
            //mMessages为头节点
            Message p = mMessages;
            boolean needWake;
            //当没有头节点,或延时为0,或者延时时间小于头节点延时时间
            if (p == null || when == 0 || when < p.when) {
                //添加新的头,如果阻塞,唤醒事件队列。
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                //以下就是根据延时的时间将Message插入MessageQueue
                for (;;) {
                    prev = p;
                    p = p.next;
                    //没有消息了break
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                //将Message插入MessageQueue中
                msg.next = p;
                prev.next = msg;
            }
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

when = SystemClock.uptimeMillis() + delayMillis SystemClock.uptimeMillis()从开机到现在的毫秒数(手机睡眠的时间不包括在内)切记不是当前的系统时间。

以上就是完成了Message入队(链表)的操作。

第三个切入点

前面说到了Looper.loop()最为第三个切入点 代码如下:

    public static void loop() {
        final Looper me = myLooper();
        //...
        for (;;) {
            //不断从MessageQueue取消息
            Message msg = queue.next(); 
            if (msg == null)  {
                return;
            }
            try {
                //回调给Handler
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            //...
            msg.recycleUnchecked();
        }
    }

上面代码比较关键的部分就是queue.next() msg.target.dispatchMessage(msg)

queue.next代码如下:

//MessageQueue
Message next() {
    //...
    for (;;) {
        //...
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // 获取现在时间
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            //...
            if (msg != null) {
                if (now < msg.when) {
                    // 下一个消息没到发送时间,设置一个唤醒时间
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 得到一个消息
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    return msg;
                }
            } else {
                nextPollTimeoutMillis = -1;
            }
            if (mQuitting) {
                dispose();
                return null;
            }
        }
        //...
    }
}

msg.target.dispatchMessage(msg)代码如下:

public void dispatchMessage(Message msg) {
  //msg.callback 是 Runnable ,如果是 post方法则会走这个 if
  if (msg.callback != null) {
    handleCallback(msg);
  } else {
    if (mCallback != null) {
        //如果 callback 处理了该 msg 并且返回 true, 就不会再回调 handleMessage
      if (mCallback.handleMessage(msg)) {
        return;
      }
    }
    //回调到 Handler 的 handleMessage 方法
    handleMessage(msg);
  }
}

小结

其实Handler总结起来就是,Handler发送Message,Message存到MessageQueue,Looper通过循环取Message,回调Handler的方法,Handler再将消息取出来执行。

时序图