美文网首页Android开发经验谈程序员Android技术知识
安卓消息机制——Handler源码浅析与手写

安卓消息机制——Handler源码浅析与手写

作者: 9dfaf364d57f | 来源:发表于2018-03-11 11:44 被阅读105次

前言

Handler大家想必已经非常熟悉,在安卓中用于线程通信,而且在面试中也会经常谈及,今天小盆友就在此聊聊Handler,加强下印象,也方便日后忘记时可查看。

目录:
1、简析Handler
2、如何达到通信
3、简单手写Handler

1、简析Handler

Handler构成

  1. Message:Handler接收和处理的消息对象。
  2. MessageQueue:消息队列。从名字上看,便知道他是使用先进先出(FIFO)的形式管理Message。
  3. Loop:用于从MessageQueue中读取Message,然后交给发送该消息的Handler处理。在Message类中有一个“Handler target”属性,用于保存发送该消息的Handler,也是处理该Message的Handler。值得注意的是一个线程只能有一个Looper。
  4. Handler:用于处理和发送消息。

浅谈Handler源码

当我们使用Handler发送一个消息(Message)时,Handler会将发送的消息时,假设此处调用了sendMessage方法进行发送,在Handler源码会经过sendMessageDelayed方法,然后再经过sendMessageAtTime方法,最后进入enqueueMessage方法,会在这个方法中,将这条消息中的target(Handler类型)属性置为当前的Handler,源码如下,在注释1⃣️处进行设置。处理消息的时候会拿取消息的这个target,然后将消息交由target处理,稍后的分析会有调用的地方,这里先有个印象,方便后面讲解。

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

经过设置之后,会将该消息加入MessageQueue中,即上面代码段的注释2⃣️处,值得注意的是queue就是我们说的消息队列,而这个队列是哪来的呢,或是说由谁实例化的呢。且看下面的一段Handler的构造函数,Handler的构造函数比较多,但是如果其参数不含有Looper类型参数的构造函数,最终都调用到下面这函数。显然,从注释3⃣️可知Handler的Looper通过Looper.myLooper()获取,而消息队列是从该Looper中获取(请看注释4⃣️),进而“猜测”(因为目前我们还没看到new的字样)消息队列是由Looper进行维护,但需要一探究竟,进入至Looper中。

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();  //3⃣️
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue; //4⃣️
    mCallback = callback;
    mAsynchronous = async;
}

进到Looper类中,其构造方法中发现消息队列的实例化语句(注释5⃣️),由此解决刚才在Handler中使用的消息队列是由谁来创建的问题。

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

这里值得注意的是Looper的构造函数只有一个而且是private,所以这里必有蹊跷,追溯其使用的地方,便看到了如下的一段代码。

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");//6⃣️
    }
    sThreadLocal.set(new Looper(quitAllowed));//7⃣️
}

想必大家非常熟悉prepare这个方法,在子线程中使用Handler都需要先调用下Looper.prepare(),而主线程不用。这是因为主线程在ActivityThread的main方法中已经通过Looper.prepareMainLooper()开启,而子线程需要自行调用,以此来初始化属于自己线程的Looper(注释7⃣️),将其保存在一个ThreadLocal类型的变量(即注释7⃣️的sThreadLocal)中,这个sThreadLocal变量便是达到Handler切换线程的核心所在,因为在注释3⃣️的Looper.myLooper()获取到的便是ThreadLocal保存该线程的Looper对象(注释8⃣️)。

略微一提的是,如果已调用该线程的Looper.prepare()再次调用的话,便会看到我们熟悉的注释6⃣️的异常。

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();  //8⃣️
}

ThreadLocal简单的说便是一个能够维护该线程数据的一个类型,各自线程只能获取到其各自线程的数据。具体介绍请各自google或百度,这里就不扩展了。

现在回过头来,Handler发送了Message,而后Message被添加至消息队列中。此时,我们会在子线程的代码最后调用了一句Looper.loop(),其代码如下,loop方法会运行一个死循环,每次循环注释9⃣️处会从消息队列中获取一个Message,这里有可能会阻塞(当消息队列中没消息时,会进行阻塞),取到消息由谁处理,就由Message持有的target来决定(请看注释🔟),这里的便呼应了我们最开始的说法(Message中target设置为发送他的Handler)。而且值得注意的是,注释🔟的dispatchMessage方法,最后会调用到我们在创建Handler中重写的handleMessage,这样便解释清楚了,整个Handler消息机制的主体流程。

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;
    ...//省略一些代码
    for (;;) {
        Message msg = queue.next(); // might block 9⃣️
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        ...//省略一些代码
        try {
            msg.target.dispatchMessage(msg); // 🔟
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        ...//省略一些代码
    }
}

小结

我们再次理清下Handler使用过程的逻辑:
1、Looper.prepare() 进行初始化Looper和Looper中的MessageQueue属性,然后ThreadLocal保存该Looper对象,同时MessageQueue是Looper的一个属性,所以也可以说一起被保存进ThreadLocal中。
2、new Handler(){...} handler被创建时,会获取创建其对象的线程的Looper和Looper中的MessageQueue,handler用于当调用sendMessage时,将Message添加到该线程的MessageQueue中。
3、Looper.loop() 开启死循环获取MessageQueue中的消息,然后交由Handler处理。

2、如何达到通信

从第一小节中,其实我们已经讲的很清楚Handler是如何达到线程间的通信的,但因为小盆友最近面试常被问到这个问题,所以觉得有必要多啰嗦几句。

这里我们就使用子线程切换回主线程(UI线程)的例子来说。
Looper被创建后,便存进了ThreadLocal,而ThreadLocal是保存当前线程的变量的一个类型。在Handler机制中,所有需要Looper的地方,都是通过Looper的静态方法myLooper来进行获取,从而得到当前线程的Looper,而后子线程想更新主线程的话,都会对主线程new出来的handler进行发送Message,因为这个handler对象内持有的便是主线程的Looper,所以当Message加入该Looper的MessageQueue之后,当从MessageQueue取出的Message时就已经是在主线程了。所以,达到切换线程的第一功应该给ThreadLocal。

3、简单手写handler

根据第一小节的分析,我们手写一个handler也是需要这四个类,各自实现的功能如下:

  1. Handler:处理和接受消息
  2. Looper:启动、退出和执行
  3. Message:保存消息信息
  4. MessageQueue:消息进队、消息出队、退出

这里的逻辑和源码分析大致类似,就不再赘述,直接上代码,再次申明这里只是简单的模拟handler,作为更好的自我理解而已。

/**
 * @author Jiang zinc
 * @date 创建时间:2018/2/4
 * @description
 */
public class Handler {

    //需要向looper的消息队列中放消息
    private final MessageQueue mQueue;

    public Handler() {
        //获取当前线程的looper
        Looper looper = Looper.myLooper();
        mQueue = looper.mQueue;
    }

    public void sendMessage(Message message){
        message.handler = this;
        mQueue.enqueueMessage(message);
    }

    public void handleMessage(Message message) {
    }
}
/**
 * @author Jiang zinc
 * @date 创建时间:2018/2/4
 * @description
 */

public class Looper {

    //用于存放在该线程中的looper,确保一条线程只有一个looper
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
    //消息队列,用于存放消息,让looper循环从中获取
    MessageQueue mQueue;


    private Looper() {

        mQueue = new MessageQueue();

    }

    /**
     *
     * @date 创建时间 2018/2/4
     * @author Jiang zinc
     * @Description 准备looper
     * @version
     *
     */
    public static void prepare(){
        if(null != sThreadLocal.get()){
            throw new RuntimeException(Thread.currentThread()+"已经有looper");
        }
        //如果没有就new一个looper
        sThreadLocal.set(new Looper());
    }

    /**
     *
     * @date 创建时间 2018/2/4
     * @author Jiang zinc
     * @Description 获取当前线程looper
     * @version
     *
     */
    public static Looper myLooper(){
        return sThreadLocal.get();
    }

    /**
     *
     * @date 创建时间 2018/2/4
     * @author Jiang zinc
     * @Description 用于循环获取消息
     * @version
     *
     */
    public static void loop(){
        //获取当前线程的looper
        Looper looper = Looper.myLooper();
        //当前线程的消息队列
        MessageQueue queue = looper.mQueue;
        for(;;){
            //获取message
            Message next = queue.next();
            //next为null,退出循环
            if(null == next){
                break;
            }
            //分发到发送message的handler 执行
            next.handler.handleMessage(next);
        }
    }

    public void quit() {
        mQueue.quit();
    }
}
/**
 * @author Jiang zinc
 * @date 创建时间:2018/2/4
 * @description
 */

public class Message {

    //消息标记
    int what;

    //消息体
    Object object;

    //下一条消息(类似链表)
    Message next;

    //发送消息的handler,需要分发到这个handler处理
    Handler handler;

    //资源回收
    public void recycle(){
        object = null;
        next = null;
        handler = null;
    }

}
/**
 * @author Jiang zinc
 * @date 创建时间:2018/2/4
 * @description
 */

public class MessageQueue {

    //消息链表
    Message mMessage;
    private boolean isQuit;

    /**
     * @date 创建时间 2018/2/4
     * @author Jiang zinc
     * @Description 将消息放入队列
     * @version
     */
    public void enqueueMessage(Message message) {

        synchronized (this) {        //因为有存有取,需要进行同步
            if(isQuit){
                return;
            }
            Message p = mMessage;
            if (null == p) {      //p为空,说明该消息队列已没有消息,放入的消息直接作为链头
                mMessage = message;
            } else {
                Message prev;
                //通过循环查找链表尾端,将消息放置链表尾
                //链表尾条件:当前message的next为null,即为链表尾
                for (; ; ) {
                    prev = p;
                    p = p.next; //此时p为prev的下一条消息
                    if (null == p) {  //p为空,说明prev为链表尾
                        break;
                    }
                }
                prev.next = message;
            }
            //通知message 解除阻塞
            notify();
        }

    }

    /**
     * @date 创建时间 2018/2/4
     * @author Jiang zinc
     * @Description 获取messageQueue的消息
     * @version
     */
    public Message next() {

        synchronized (this) {
            Message message;
            for (; ; ) {
                if(isQuit){
                    return null;
                }
                message = mMessage;
                if (null != message) {
                    break;
                }
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //将消息的表头往后移
            mMessage = mMessage.next;
            return message;
        }

    }

    public void quit() {
        synchronized (this) {
            isQuit = true;
            Message message = this.mMessage;
            while (null != message) {
                //获取下一个message
                Message next = message.next;
                message.recycle();
                //将message指向下一个
                message = next;
            }
            notify();
        }
    }

}

最后进行调用:

/**
 * @author Jiang zinc
 * @date 创建时间:2018/2/4
 * @description
 */

public class Main {

    public static void main(String []args){
        Looper.prepare();

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

        new Thread(){
            @Override
            public void run() {
                System.out.println(Thread.currentThread()+" send");
                Message message = new Message();
                message.object = "Zinc Power";
                handler.sendMessage(message);
            }
        }.start();

        final Looper looper = Looper.myLooper();
        new Thread(){
            @Override
            public void run() {
                try {
                    sleep(1_000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                looper.quit();
            }
        }.start();

        Looper.loop();
    }

}
运行结果

写在最后

至此,本片文章就完结了,撒花。如果喜欢的话,给个心吧。

相关文章

网友评论

    本文标题:安卓消息机制——Handler源码浅析与手写

    本文链接:https://www.haomeiwen.com/subject/kqemzxtx.html