Android 的消息机制Handler,是在开发过程中都会用到的,它可以轻松的从子线程切换到主线程,大部分的时候我们将Handler会用在更新UI的操作.那么Handler是如何做到不同线程通信的呢? 如何切换线程的呢?本篇文章带领大家来轻松的实现一个Handler,并深入的去了解Handler的实现原理

在讲解之前,我们先来了解一下Handler的工作过程,至于如何使用Handler,这里我就不在复述了,只要做过Android开发我相信大家都会使用.

Handler的工作过程

Handler发送消息是通过Looper以及MessageQueue协同工作的.
Looper的初始化:在应用启动时ActivityThread会创建一个Looper.prepare(),并调用Looper.looper()方法无限循环等待是否有新的消息.
发送消息:通过Handler的send方法发送消息,会调用MessageQueue.enqueue()方法,此时消息(Message)会被加入MessageQueue消息队列中,已知在Looper初始化是,Looper.looper()一直在监听是否存在新的消息,此时Looper发现有新消息到来,就会处理该消息,最终会调用Handler.handleMessage()方法,Looper是运行在创建Handler的线程中的,handleMessage一定在创建Handler的线程中去执行.

这个过程用图来表示
image.png

通过上述简单的解了Handler的工作过程,接下来我们就根据上述过程来实现一个简单的Handler,然后我们在带着问题去看Handler的源码.

手写一个Handler

  • 首先创建四个类:Handler、Message、MessageQueue、Looper

我们先实现核心的Looper类:
通过一个静态的ThreadLocal来存储Looper对象,prepare()方法来初始化looper

//存储Looper
    private static final ThreadLocal<Looper> mThreadLooper = new ThreadLocal<>();

    //looper中存在一个消息队列
    public MessageQueue messageQueue;

    public Looper() {
        messageQueue = new MessageQueue();
    }

    /**
     * 准备 准备时就是在主线程中准备的
     */
    public static void prepare() {
        //ThreadLocal 会拿到当前的线程
        if (null != mThreadLooper.get()) {
            throw new RuntimeException(Thread.currentThread() + ":已经有了looper");
        }
        mThreadLooper.set(new Looper());
    }

然后再写一个获取Looper的静态方法myLooper()主要是给Handler调用

/**
     * 获得当前线程的looper
     *
     * @return
     */
    public static Looper myLooper() {
        return mThreadLooper.get();
    }

Looper还有一个核心的方法,就是实现轮询器去查询新的消息,我们知道要通过消息队列MessageQueue来查询是否有新的消息,MessageQueue存储的为Message,我们先来实现Message,Message类为链式结构

public int what;

    public Object obj;

    //下一个消息
    public Message next;

    //使用此handler发送的消息,则需要分发到这个handler处理
    public Handler target;

    public void recyle() {
        obj = null;
        next = null;
        target = null;
    }

MessageQueue来存储Message,MessageQueue需要实现两个方法,一个是加入队列enqueue,一个是获取尾部的队列next,Looper轮询去就是调用next方法来获取最新的消息,当next没有最新消息时,处于阻塞状态wait(),当有入队enqueue操作时,需要通知next不用处于阻塞状态了notify(),我有新消息了,注意入队和取队需要同步synchronized.

入队操作

    /**
     * 将消息塞入队列
     *
     * @param message
     */
    public void enqueue(Message message) {
        //有入队就有取队 需要同步一下
        synchronized (this) {
            Message m = mMessage;
            if (null == m) {
                mMessage = message;
            } else {
                //循环判断是否在链表尾端
                Message per;
                do {
                    per = m;
                    m = per.next;
                } while (null != m);
                per.next = message;
            }
            //通知获取 message解除阻塞
            notify();
        }
    }

取队操作

    /**
     * 获取消息
     *
     * @return Message
     */
    Message next() {
        synchronized (this) {
            Message message;
            for (; ; ) {
                message = this.mMessage;
                if (null != message) {
                    break;
                } else {
                    //等待 阻塞状态
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            mMessage = mMessage.next;
            return message;
        }
    }

MessageQueue我们已经实现了,接下来可以实现Looper的轮询器了,来监听新消息

/**
     * 不停的从MessageQueue中取出Message
     */
    public static void looper() {
        Looper looper = Looper.myLooper();
        MessageQueue messageQueue = looper.messageQueue;
        for (; ; ) {
            //不停的获取message
            Message message = messageQueue.next();
            if (message == null) {
                break;
            }
            //分发到发送message的handler执行
            message.target.handleMessage(message);
        }
    }

接下来,实现Handler方法,我们在Handler中获取Looper,并通过Looper获取MessageQueue发送消息时加入消息队列.

public class Handler {
    private Looper looper;

    private MessageQueue messageQueue;

    public Handler() {
        looper = Looper.myLooper();
        //拿到当前线程的消息队列
        messageQueue = looper.messageQueue;
    }

    public void sendMessage(Message message) {
        message.target = this;
        //将此消息加入消息队列
        messageQueue.enqueue(message);
    }

    public void handleMessage(Message message) {

    }
}

整体的消息机制代码,我们已经实现了,接下来模拟一下handler的发送过程,第一步初始化Looper,然后开启Looper轮询器监听消息,Handler发送接收消息

  public void testHandler() {
        Looper.prepare();//looper 处于准备状态

        final Handler handler = new Handler() {
            @Override
            public void handleMessage(Message message) {
                System.out.println(Thread.currentThread() + " ");
            }
        };

        new Thread(new Runnable() {
            @Override
            public void run() {
                handler.sendMessage(new Message());
            }
        }).start();

        Looper.looper();
    }

OK,通过上述手写实现简单的handler,相信读者一定明白了,Handler的核心思想.

Handler源码分析

彻底明白Handler的工作过程后,我们就能很彻底的看懂Handler源码的实现了

还是按照上面,手写Handler的实现来分析Android Handler源码.

Looper源码分析

首先看到,熟悉的成员属性

// sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;

再来看prepare()方法的实现,将Looper存储到ThreadLocal中去

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

同样也实现了myLooper方法,来提供其他类调用Looper

 /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

Android是如何初始化Looper呢?来看ActivityThread源码的main方法,代码如下,对Looper初始化,并开启了轮询器

 public static void main(String[] args) {


       .......

        Looper.prepareMainLooper();


        .....
        Looper.loop();
    }

prepareMainLooper方法很简单,实际上调用了prepare和myLooper方法

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

轮询器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.");
        }
        final MessageQueue queue = me.mQueue;

       ........
        //无限循环获取Message
        for (;;) {
            Message msg = queue.next(); // might block 没有新的消息就不执行
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
             final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            try {
 //如果存在新消息获取target 就是当前的Handler 然后调用dispatchMessage处理消息
 msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            .....
            }

            ......
            }

再来看Handler的dispatchMessage,实际上就是处理消息

public void dispatchMessage(Message msg) {       
        //如果message的callback实际上是一个Runnable,就调用run方法
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            //如果Handler设置了回调就调用回调
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //直接调用handleMessage
            handleMessage(msg);
        }
    }

Handler源码分析

主要看handler发送消息的代码,Handler在初始化时,构造函数中,获取了Looper和MessageQueue,

public Handler(Callback callback, boolean async) {
        ......

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

Handler中发送消息有好几种,我们只看最简单的一个

public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

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

上述代码实现还是非常简单的,实际上就是将消息添加到消息队列中.

MessageQueue源码

再来看一下MessageQueue的入队和出队
入队操作代码入下,实际上就是添加到Message单链表的尾部

boolean enqueueMessage(Message msg, long when) {
        //判断msg当前的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;
            Message p = mMessages;
            boolean needWake;
            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;
                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;
            }

            // 这段代码就是通知 next不用处于阻塞状态了,有新的消息过来了,注意这里并没有用notify()方法,而是使用底层C++的一个方法,想要了解的可以看底层的源码
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

再来看一下next方法,此方法也是Looper.loop()轮询器轮询判断是否有新消息的核心方法

synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
                }

OK,以上就是整个Handler源码工作的核心代码,至于ThreadLocal,我会单独的一篇文章讲解.