Android异步消息机制初探

Android中,当我们需要在子线程中进行网络请求等耗时操作后,如果需要更新UI时,通常会考虑使用Handler来处理,一种常用的写法如下:

private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 1){
                // update ui
            }
        }
    };

new Thread(new Runnable() {
            @Override
            public void run() {
                Message message = Message.obtain();
                message.what = 1;
                mHandler.sendMessage(message);
            }
        });

除了上边我们用到的Handler类外,Android消息机制要正常工作还需要LooperMessageQueue两个类的配合,下来我们分别介绍三个类,以及它们之间的工作原理:

1、MessageQueue

顾名思义就是消息队列,但其实MessageQueue的底层是通过单链表来实现的,MessageQueue是用来中转Handler对象发送出的消息,主要包括enqueueMessagenext两个操作,分别用来插入一条消息和取出一条消息,因为链表在插入和删除操作上有较高的效率,这可能就是使用该数据结构的原因吧。MessageQueue只负责保存消息,并不会处理消息,MessageQueue对象如何得到呢?继续往下看。

2、Looper

如果我们是在UI线程中使用Handler来发送异步消息,系统已经在当前UI线程通过Looper.prepareMainLooper()帮助我们创建好Looper对象,并调用Looper.loop()开启无限消息循环,不断从MessageQueue的实例中读取消息。如果在子线程中我们不手动创建Looper对象,则会抛出java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()的异常,所以在子线程中创建并使用Handler对象,就必须手动调用Looper.prepare()创建Looper对象,并通过Looper.loop()开启消息循环。
在子线程中可通过如下方式使用Android消息机制:

new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();//创建Looper对象
                Handler handler = new Handler();
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        //do something
                    }
                });
                Looper.loop();//开启消息循环
            }
        });

这样子线程同样具有了异步消息循环处理的能力。
我们先通过源码分析一下Looper.prepare()方法

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

很简单,只有一行代码,继续进入到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));
    }

重点看一下new Looper()方法

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

可以看到在创建Looper对象时也创建了MessageQueue对象,此时Looper对象持有了MessageQueue对象的引用。并与当前线程绑定,保证一个线程只会有一个Looper对象,同时一个Looper对象也只有一个MessageQueue对象。

3、Handler

首先看一下Handler的构造方法

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

依然只有一行,继续跟进

public Handler(Callback callback, boolean async) {
       ........
        省略
       ........
        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 = Looper.myLooper(),我们Handler对象拿到了对应Looper对象的引用,产生了关联。
可以看到如果Looper等于null的话会抛出异常,这也验证了上边第二点中的说法,创建Handler对象时必须有Looper对象存在。
再看 mQueue = mLooper.mQueue这一行,其实就是将Handler的实例与我们Looper实例中创建的MessageQueue对象关联起来。此时MessageQueue对象已经和Looper对象以及Handler对象关联了起来,并且他们在同一线程中。

4、工作原理

结合文章开头的例子来分析 Handler 具体的工作原理。

通过Handler对象发送消息时可用的方法非常多,例如常用的sendMessage系列方法,post系列方法等,通过源码可以知道,这两系列方法都最终都是调用下边的方法来实现的:

public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

继续看下sendMessageAtTime方法

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

首先看第一行msg.target = this,将Handler对象赋值给的Message对象的target属性,此时Message对象持有了Handler对象的引用。
最后一行通过调用MessageQueue 对象的enqueueMessage方法来保存发送的消息。

上边Looper类中还有一个Looper.loop()方法没分析,调用该方法后,则会一直检测MessageQueue对象中是否有数据,有的话则取出,否则阻塞,此时我们来看一下源码:

public static void 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
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);
            ........
            省略
            ........
        }
    }

只看核心的代码,首先是一个无限的for循环,循环中调用MessageQueue 的next方法不断的取出Message对象,没有消息则进入阻塞状态。否则执行msg.target.dispatchMessage(msg),这里的msg.target就是上边enqueueMessage方法中的msg.target,也就是Handler对象的引用,所以此时消息就可以调用Handler的dispatchMessage()方法进行消息处理。此时Handler对象发出的消息在MessageQueue对象中通过 Looper 完成了中转,由于Looper是在主线程创建并开启消息循环的,所以dispatchMessage()在主线程被调用:

接下来看一下dispatchMessage方法:

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

首先看第一行if判断,什么时候msg.callback != null成立呢,其实就是当我们通过如下形式发送消息时

handler.post(new Runnable() {
                    @Override
                    public void run() {
                        // update ui
                    }
                });

继续查看post方法

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

进入getPostMessage方法中

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

记住m.callback = r这一行,此时再看dispatchMessage中第一个if条件的执行代码:

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

方法中执行的正是callback 中的run方法

if (mCallback != null)什么时候成立呢?当我们通过如下方式使用Handler时成立

private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            return false;
        }
    });

从而下边的代码得到执行

if (mCallback.handleMessage(msg)) {
                    return;
                }

即就是我们上边new Handler.Callback()中的handleMessage方法

最后如果我们采用文章最开始的代码执行handler操作则会执行dispatchMessage中最后一行handleMessage(msg)方法:

public void handleMessage(Message msg) {}

可以看到是一个空的方法,因为需要我们自己去实现哦!同时 Handler 也是在主线程创建的,所以 handleMessage()方法最终在主线程执行,这样就完成了从子线程到主线程的切换。

最后说一下,创建Message对象,可以通过new方法 ,也可以使用Message.obtain()方法,但建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new 重新分配内存。

到这里你有没有发现LooperMessageQueueHandler之间的关系呢?

  1. 在Looper中创建了MessageQuene,Looper循环从MessageQuene取出消息,由于消息持有Handler的引用,最终会调用Handler的handleMessage()方法或其它方法,Looper会被保存在ThreadLocal中,一个线程对应唯一Looper。
  2. Handler发送消息时,会从ThreadLocal中取出对应线程的Looper,再从中得到对应的MessageQuene,发送的消息会持有Handler的引用,然后将消息保存到MessageQuene。
  3. 关于ThreadLocal在下一篇有讲到

到此Android消息机制的基本原理已经分析完了,简单的概括一下:通过Handler对象的相关方法将Message对象发送出去进而插入到MessageQueue对象中,然后Looper对象调用loop方法,进一步会执行MessageQueue对象的next方法取出消息,交给Handler对象的dispatchMessage方法来处理。这就是Android消息机制一个大致的流程。

推荐阅读更多精彩内容