Handler消息机制源码阅读

1,067 阅读6分钟

Android中,线程间通信最常见的方式是使用Handler消息机制。Handler 和 Looper、Message、MessageQueue 共同协作组成一个完整的Handler消息机制。下面我们通过源码来看看Handler是如何完成消息的收发的。

常见用法

先来看一下常见用法,然后跟着用法一步步在阅读源码。 1、初始化一个Handler对象

    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
        }
    };

2、发送消息

//发送一个空消息
mHandler.sendEmptyMessage(1);

源码阅读

Handler 构造方法

第一步就是初始化一个Handler对象,那么我们没有理由不去看一下Handler的构造方法了。

    public Handler() {
        this(null, false);
    }

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

从上面构造方法可以看到,我们在构造方法充初始化了mLooper 、mQueue 、mCallback 对象。

并且 mLooper 必须初始化,否则会报 RuntimeException 异常。还有后面的 mQueue 也是通过Looper获 得的,那么我们来看一下 Looper 初始化方法吧 mLooper = Looper.myLooper();

初始化 Looper 对象

查看Looper源码,可以看到官方的一个Looper 配合Handler 使用的例子。

在这里插入图片描述
Looper中声明的全部变量
在这里插入图片描述

1、Looper.prepare()
    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));
    }

prepare()方法中创建一个 Looper 对象并保存在 sThreadLocal 中,当我们多次调用 prepare()方法创建Looper 对象时,会抛出 RuntimeException 异常,保证每个线程中只有一个 Looper对象

Looper 的构造方法如下:

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
  • 在构造方法中创建一个MessageQueue 对象。因为 Looper的唯一性,所以 MessageQueue在一个线程中也是唯一的。
  • 获得创建 Looper 的线程,将Looper和线程进行绑定
2、Looper.loop()

上面通过 Looper.prepare() 创建并保存了一个 Looper 对象,同时创建了一个MessageQueue 对象。下面我们来看一下 Looper.loop() 方法的源码

    public static void loop() {
        final Looper me = myLooper();
        //必须先调用 Looper.prepare() 初始化 Looper对象,才能调用loop方法
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //调用Looper.prepare() 时,在Looper 的构造方法中创建了一个mQueue 实例
        final MessageQueue queue = me.mQueue;

		//无限循环不停获取 MessageQueue 中的消息
        for (;;) {
        	//调用 MessageQueue 的 next()方法获得 Message 对象
            Message msg = queue.next(); // might block
            if (msg == null) {
                // 当 Message 为空时 阻塞等待
                return;
            }
			...//省略部分
            try {
            	//当有消息时,调用msg的target(Handler对象)的dispatchMessage()方法分发消息
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            //消息分发完成后,回收消息内容
            msg.recycleUnchecked();
        }
    }

从源码可以看到,loop()方法主要做了以下3件事:

  • 校验 Looper 对象是否初始化,即有没有执行 Looper.prepare() 方法 。如果没有则抛出 RuntimeException
  • 实现一个无限循环,不断获取 MessageQueue 中的消息,当消息为空时阻塞,当消息不为空时,调用handler的dispatchMessage() 方法分发消息
  • 回收分发的消息内容

Looper中还有个prepareMainLooper()方法,是专门给主线程初始化使用的,一会就会用到。

Looper中的主要方法和作用就这些,说了半天好像我们第一部分常见用法并没有调用 Looper.prepare() 和 Looper.loop() 。

其实在我们的Activity的创建的时候Activity中已经为我们初始化好了一个Looper对象。

在ActivityThread 中初始化Looper

其实我们的Activity中也有handler线程切换,比如常用的 runOnUiThread 就是使用handler进行线程切换的。所以Activity早已经为我们在主线程中初始化好了一个Looper对象

在这里插入图片描述

在ActivityThread中的main()方法中初始化Looper

在这里插入图片描述
所以如果在主线程中,我们直接使用Handler就行了,因为每个线程中Looper对象是唯一的,所以如果我们在主线程调用 Looper.prepare() 的话,会抛出 RuntimeException 异常
在这里插入图片描述

发送消息

sendMessageAtTime

Handler 和 Looper初始化完成后,我们就可以发送消息了。发送消息的源码如下:

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

其实不管我们发送空消息或者非空消息,延时或者不延时发送,最终都会调用上面的sendMessageAtTime()方法。 mQueue 在Handler构造方法中已经初始化完成,下面我们看一下 enqueueMessage()方法源码

handler的enqueueMessage
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    	//将Handler赋值msg中的target对象。
    	//上面Looper获得消息后,会调用msg.target.dispatchMessage()分发消息
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //调用 MessageQueue.enqueueMessage方法将消息入队列
        return queue.enqueueMessage(msg, uptimeMillis);
    }

enqueueMessage方法主要做了两个件事:

  • 将此handler对象赋值给Message中的target。后面会调用target.dispatchMessage()分发消息
  • 调用MessageQueue的入队方法,将消息放入消息队列

下面看一下 MessageQueue的入队列方法enqueueMessage

MessageQueue.enqueueMessage()入队列
    boolean enqueueMessage(Message msg, long when) {
    	//handler对象不能为空
        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 队尾结点,将队尾结点赋值给P
            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;
                //消息队列为空时,直接将msg赋值给队尾结点 mMessages
                mMessages = msg;
                needWake = mBlocked;
            } else {
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                //当消息队列不为空时,记录上一个结点的消息
                Message prev;
                for (;;) {
                	//上一个消息
	                Message prev;
	                for (;;) {
	                	//将队尾结点赋给上一个结点
	                    prev = p;
	                    //将队尾向后移动一位
	                    p = p.next;
	                    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;
    }

enqueueMessage方法其实就是一个队列的入队操作。当消息入队完成后,MessageQueue 就有了消息,同时在我们的Looper.loop()的无限循环方法中就能获得一个不为空的Message对象,然后调用msg.target.dispatchMessage(msg);分发数据

dispatchMessage()数据分发

说了这么多次的dispatchMessage方法,现在终于到了查看dispatchMessage的源码的时候了,在handler中很靠前的方法。

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

从源码可以看出当 msg.callback和 mCallback 为Null的时候,会调用 handleMessage()方法。很明显到目前为止我们并没有为msg.callback 和 mCallback 赋值,所以会调用 handleMessage()方法。同时我们在Activity创建Handler的时候已经覆写了handleMessage()方法,这样我们就可以在handleMessage方法中接收消息了。

handleMessage

在这里插入图片描述
handleMessage方法就是我们在创建Handler的时候覆写的方法。

mHandler.post()

        mHandler.post(new Runnable() {
            @Override
            public void run() {
            }
        });

Handler还有一种post用法,源码如下:

在这里插入图片描述
getPostMessage方法如下:
在这里插入图片描述
返回一个Message对象,并对Message中的callback赋值。

sendMessageAtTime上面已经说过了,消息入队列,loop循环中发送消息,调用msg.target.dispatchMessage()分发消息。

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

此时 msg.callback 不为空,执行handleCallback()方法,源码如下:

    private static void handleCallback(Message message) {
        message.callback.run();
    }

然后在我们new的Runnable中收到回调。

Handler机制执行流程图

结束

到这里Handler机制相关源码就结束了,还好前段时间看了数据结构,又正好看到队列了,这里就遇到消息队列了☺。虽然阅读的有点晚,但只能慢慢赶上了。加油!