Android中Handler原理的源码分析

2019-02-25  本文已影响0人  全汪汪

前言

往往我们看源码时都会无从下手,不知道应该先从哪里看,也不知道在庞大的源代码面前哪些才是我们需要的,这里我自己总结了一些方法,现在前言里说明一下。
方法一:百度相关原理图,结合原理图中流程的步骤,一个个去代码里找并分析
方法二:从我们平时使用代码的顺序来找到源码入口然后再深入分析
这里使用的第二个方法结合第一个方法的来分析handler的源码。

Handler的使用

首先我们都首先必须知道Handler消息机制的原理,再结合我们使用的代码进行分析。原因是我们平时如果再主线程使用handler的话会隐性调用两个方法,如果我们不知道的话就无法做出正确的分析。这两个方法就是
Looper.prepare()
以及
Looper.loop()
只有按顺序的调用这两个方法后才能使用handler

Handler的原理

原理图在简书上有很多,大家可以自行查找,在这里只做简述
1.涉及三个角色:Messagequeue,Handler,Looper
2.三个角色功能:Messagequeue消息队列,Handler处理消息,Looper拿去消息给Handler处理
3.流程:创建使用Looper.prepare()创建Looper,同时在其内部会创建Messagequeue消息列表。然后创建Handler复写接口(自动绑定到创建线程所拥有的Looper),最后通过Looper.loop()不断拿取消息并发送给Handler处理

源码分析

消息获取并处理

Looper.prepare()

Handler使用前必先调用的代码之一

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

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            //注意这里,如果已经创建过Looper会报错
            throw new RuntimeException("Only one Looper may be created per thread");
        }
          //创建一个Looper
        sThreadLocal.set(new Looper(quitAllowed));
    }

事实这段代码就是创建一个Looper并且检测是否已创建的,如果在同一个线程里创建多个就会报错。
那为什么不直接使用new Looper() 呢?因为他只有一个私有的构造函数

 private Looper(boolean quitAllowed) {
        //创建一个MessageQueue
        mQueue = new MessageQueue(quitAllowed);
        //引用当前线程
        mThread = Thread.currentThread();
    }

同时我们可以发现,在创建Looper的使用也同时创建了一个MessageQueue消息列表,并且获得了当前线程的引用。sThreadLocal.set()是什么
,为什么能检测一个线程重复创建的问题呢?这里暂且不解释,放到后面扩展。

Looper.loop()

紧接着prepare调用之后的必调用代码就是Looper.loop()同样是在Looper这个类里:

    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
      //从Looper中获取消息列表
        final MessageQueue queue = me.mQueue;
        ...
        //无限循环知道消息队列为空
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
          ...
            try {
                //注意这里,调用了一个dispatchMessage方法
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
         ...
            msg.recycleUnchecked();
        }
    }

这里我们看到在代码开始就拿到Looper并且检测是否为空,如果没建立Looper就直接调用loop方法就会报错,这就是一定要按代码顺序调用的原因,检测空的目的是一位内后面会从Looper中获取消息列表(第一个注释处),然后无限循环列表逐一拿出消息Message,再通过Message对Handler的引用,调用dispatchMessage方法分发到Handler中,让Handler处理。

 public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
          //调用Handler的callback,实际上就是我们创建Handler时复写的那个接口
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //空方法,实现要靠子类的实现,如果不实现接口就默认走子类实现的方法
            handleMessage(msg);
        }
 /**
     * Callback interface you can use when instantiating a Handler to avoid
     * having to implement your own subclass of Handler.
     */
    public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        public boolean handleMessage(Message msg);

以上就是消息获取并处理的源码,即通过Looper获取Messagequeue,再通过他来获取Message消息中的Handler引用,最后调用dispatchMessage来调用我们定义Handler时覆写的方法,达到消息处理的效果。那问题来了,既然这个过程是通过Message来获得Handler的引用的,那么Message和Handler的联系点在哪?别急,我们接下来看Message消息的发送源码。

消息发送

Handler创建构建与Lopper和Messagequeue的联系

  public Handler(Callback callback, boolean async) {
       ...
      //根据当前线程获取Lopper引用
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        //获取Messagequeue引用
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;

在创建Handler对象的时候会自动与当前线程的Lopper挂钩。

handler.sendMessage()

不外乎所有的sendMessage,无论是发送空消息还是具体消息,最终都会走到一个Handler中的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);

最后无一例外的走到一个enqueueMessage()方法,这个方法是用来将消息放入队列的。

 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //这里将传递过来的msg绑定当前的Handler,即Message和Handler在这里产生关系。
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //调用队列的enqueueMessage方法,将方法传递到列表中去
        return queue.enqueueMessage(msg, uptimeMillis);  

在Messagequeue里的enqueueMessage方法,就是一些消息插入的代码,然后走的native一些相关方法,有兴趣的可以看下(然鹅我并不懂)

总结

至此从消息发送到消息拿去并处理的大体上的源码已经全部初步解析,总的来说是以下的步骤:
1.调用Looper.prepare(),创建线程必要的Looper和Messagequeue(会自动和当前线程挂钩)。一个线程只能拥有一个Looper,从上面检测的代码可以看到。
2.创建Handler,并覆写处理消息的接口实现其dispatchMessage方法(自动与当前线程的Looper和Messagequeue挂钩,没有的话会报错)。
3.通过Hander.sendMessage方法来发送消息(这个过程msg会正式和当前handler挂钩,使其拥有handler的引用)
4.调用Looper.loop()方法开启死循环,遍历Messagequeue拿去msg,并使用msg中handler的dispatchMessage方法将msg发送到handler中去。
5.dispatchMessage方法调用我们创建Handler时覆写的接口,完成消息处理。

一个线程只能有一个Looper,一个Looper对应一个Messagequeue,一个Handler也只能对应一个Looper,但是一个Lopper却可以对应多个Handler(即可以有多种不同的处理方法)

问题:

1.Looper.prepare()中如何保证Looper时线程唯一的?
2.即然主线程是默认调用了Looper.prepare和Looper.loop两个方法,那么在loop方法无限循环中为何不会导致主线程ANR(主界面卡死)?
3.loop中拿取消息的无限循环中可以看到,如果没消息会中断循环,中断后如何重启循环并拿到新的消息?
4.在创建Handler覆写接口时,dispatchMessage返回的布尔值有什么用?
5.Messagequeue中的数据结构是什么?

结尾

问题的话下章再说,其实有些我也不是很懂,得研究研究...
有错误欢迎指正并讨论,上述分析纯属个人观点

上一篇下一篇

猜你喜欢

热点阅读