美文网首页
最易懂的Handler工作机制源码分析

最易懂的Handler工作机制源码分析

作者: Dapengyou | 来源:发表于2019-02-20 00:45 被阅读4次
    Handler.png

    这篇文章将会从以下几点对Handler进行分析

      1. 如何使用Handler
      2. Handler、Looper、MessageQueue如何建立关系
      3. Handler发送消息后进行了哪些操作
      4. Handler如何获取消息
    

    如何使用Handler

    在主线程中创建Handler实例

     private Handler mAnimatorHandler;
     private void initHandler() {
            mAnimatorHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                      ......
                }
            };
        }
    

    根据代码需求在适当的时机 通过Handler发送消息到消息队列中

    mAnimatorHandler.sendEmptyMessage(0);
    

    这里Handler的使用不是本章的重点
    如果不是很清楚Handler的使用,这里有很好的blog

    Handler、Looper、MessageQueue如何建立关系

    进入Handler构造方法
     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();//创建looper对象
            if (mLooper == null) {//判断looper
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;//创建一个MessageQueue,通过looper来赋值,handler和looper共用一个消息队列
            mCallback = callback;
            mAsynchronous = async;
        }
    
    

    从上面的操作和代码,我们可以看到在Handler构造方法中,又使用Looper.myLooper()方法创建了Looper对象,并判断Looper是否为空,若为空则抛出异常,异常内容是“没有调用 Looper.prepare()方法”,所以可以判断Looper.prepare() 在Looper中是个很重要的方法,若Looper不为空,则创建一个MessageQueue,通过Looper中的MessageQueue来赋值,自此Handler和Looper共用一个消息队列。

    接下来我们去看Looper.prepare()方法

    调用Looper中prepare() 方法创建Looper对象,才能保证之后Handler发送的消息添加到队列中

     private static void prepare(boolean quitAllowed) {
            if (sThreadLocal.get() != null) {//不为空说明Looper被创建好了,不用再创建了
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            sThreadLocal.set(new Looper(quitAllowed));//将没有创建好线程的looper对象放入ThreadLocal容器中,以便下次能够使用
        }
    

    从源码中我们可以看到在Looper构造方法中创建MessageQueue对象

    private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);//创建MessageQueue对象
            mThread = Thread.currentThread();
        }
    
       public static @Nullable Looper myLooper() {
            return sThreadLocal.get();//获取looper对象
        }
    

    sThreadLocal是一个线程池

    下面是找到这些源码的步骤:


    查看源码的步骤

    至此Handler、Looper、MessageQueue已经建立的关系,在创建Handler的同时创建了Looper对象,在Looper对象内部又创建了MessageQueue对象,并且Handler和Looper共用一个MessageQueue。

    Handler发送消息后进行了哪些操作

    当Handler调用sendEmptyMessage方法后又发生了什么,为什么主线程可以接收到发送的消息,下面我们带着这个问题来查看发送消息后的源码

    发送消息后的源码查看

    通过以上操作我们发现在Handler中有很多发送消息的方法:

    • sendMessage(Message msg)
    • sendEmptyMessage(int what)
    • sendEmptyMessageDelayed(int what, long delayMillis)
    • sendEmptyMessageAtTime(int what, long uptimeMillis)
    • sendMessageDelayed(Message msg, long delayMillis)
    • sendMessageAtTime(Message msg, long uptimeMillis)
    • sendMessageAtFrontOfQueue(Message msg)

    不管是哪种发送消息的方法,都会走到MessageQueue类中enqueueMessage() 方法,这个方法的作用是向消息队列中插入消息,下面是enqueueMessage() 方法中插入消息的代码:

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

    另外MessageQueue类中还有一个重要的方法是next方法,作用是读取MessageQueue中的消息并删除这条消息,此方法在后面的轮询消息队列中会使用到。

    Handler如何获取消息

    发送消息后,将消息写入MessageQueue后,要怎么获取消息呢?
    通过前面的分析我们知道Looper管理MessageQueue,因此在Looper的源码中所开放的方法中我找到loop() 这个方法是最重要的,此方法使用死循环的方式轮询消息队列,获取消息,虽然MessageQueue叫做消息队列,但实际上它的数据结构是一个单链表结构,下面是loop() 方法的重要部分的代码:

     public static void loop() {
            final Looper me = myLooper();//获取Looper对象
            if (me == null) {//为空说明  Looper.prepare()方法没有被调用
                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
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
                ......
                 try {
                    msg.target.dispatchMessage(msg);
                    dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
            }
            
    

    既然loop() 方法是个死循环那么怎么跳出循环呢?Looper中有两个退出的方法,一个是quit(),另一个是quitSafely(),这两个方法的区别是quit会直接退出Looper,而quitSafely是设定了一个标记,当消息队列中的已有消息处理完毕后才安全的退出。所以想跳出循环,就得调用退出的方法。

    查看轮询队列的相关源码

    从源码可以看出msg.target是一个Handler

    接着我们进入Handler中 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是一个Runnable

     private static void handleCallback(Message message) {
            message.callback.run();//在子线程做一些操作,执行run方法
        }
    

    当我们进入Callback接口中,我们终于见到了熟悉的handleMessage() 方法

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

    到这里我们就从Handler的创建到消息的发送再到接收消息的整个流程走完了

    handler结构图.png

    最后,我还有一个问题那就是prepare() 方法是在哪里被调用的呢?带着这个问题,我们再去看看Looper类中的源码

     /** Initialize the current thread as a looper.
          * This gives you a chance to create handlers that then reference
          * this looper, before actually starting the loop. Be sure to call
          * {@link #loop()} after calling this method, and end it by calling
          * {@link #quit()}.
          */
        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));
        }
    
        /**
         * Initialize the current thread as a looper, marking it as an
         * application's main looper. The main looper for your application
         * is created by the Android environment, so you should never need
         * to call this function yourself.  See also: {@link #prepare()}
         */
        public static void prepareMainLooper() {
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
        }
    
    

    哦,原来是在prepareMainLooper() 方法中调用的,prepareMainLooper() 方法主要是给主线程也就是ActivityThread创建Looper使用,ActivityThread被称为UI线程,也就是主线程。Looper中还提供了一个getMainLooper() 方法,通过它我们可以在任何地方获取到主线程的Looper。

    相关文章

      网友评论

          本文标题:最易懂的Handler工作机制源码分析

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