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

2019-02-20  本文已影响4人  Dapengyou
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中有很多发送消息的方法:

不管是哪种发送消息的方法,都会走到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。

上一篇下一篇

猜你喜欢

热点阅读