美文网首页程序员
Handler源码解析系列一

Handler源码解析系列一

作者: 丸子哒哒哒 | 来源:发表于2019-04-28 15:46 被阅读0次

    版权声明

    版权声明:本文为博主原创文章,转载请注明出处+地址

    什么是Handler消息机制

    Android内部的Handler消息机制,是将子线程处理的结果,通过Handler异步回调给主线程,让主线程去进行操作的一个机制。

    Handler消息机制的工作流程图

    Handler调用流程图.png

    Handler的源码解析

    Handler使用案例

    首先,给大家简单的展示下Handler的使用流程代码

    /**
     * Function:简述Handler使用方式
     *
     * @author wanzi Created on 2019/4/28
     */
    public class HandlerActivity extends AppCompatActivity {
        private MyHandler mHandler;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mHandler = new MyHandler();
            Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
                @Override
                public void run() {
                    Message msg = new Message();
                    msg.what = 1;
                    mHandler.sendMessage(msg);
                }
            }, 5000, TimeUnit.MILLISECONDS);
        }
    
        private static class MyHandler extends Handler {
    
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                if (msg.what == 1) {
                    Log.d("HandlerTestActivity", "Handler发来贺电");
                }
            }
        }
    }
    

    Handler是如何将消息加入到消息队列?

    分析源码,我们首先要找到切入点,mHandler.sendMessage(msg),代码从这个函数开始调用的handler,那么继续往下追踪,我们来研究下,在发送消息之后,Handler干了什么?
    Handler.java

    public final boolean sendMessage(Message msg)
        {
            return sendMessageDelayed(msg, 0);
        }
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
        {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }
      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方法,根据字面意思,就是加入消息队列,那我们继续看里面的实现
    Handler.java

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    
    • 将当前的handler对象赋值给了msg.target
    • 调用了messagequeue的enqueueMessage方法
      MessageQueue.class
     boolean enqueueMessage(Message msg, long when) {
           ...
    
            synchronized (this) {
             ...
                if (p == null || when == 0 || when < p.when) {
               ...
                } else {
                    ...
                    msg.next = p; // invariant: p == prev.next
                    prev.next = msg;
                }
    
              ...
            }
            return true;
        }
    

    到此为止,msg算是终于成功的加入到消息队列当中,当然MessageQueue.enqueueMessage的内容远不止这么简单,里面包含一些异常处理,唤醒机制等等,单独写一篇文章详细的讲解。

    Handler如何从消息队列获取消息?

    主线程的Looper如何创建?

    说到获取消息,我们还得从Looper说起,大家都知道,在主线程中创建的Handler对象,是自带Looper对象的,那么主线程中的Looper是从哪里创建的呢?
    我们知道,程序启动开始的入口在ActivityThread.class的main函数,我们来看看代码
    ActivityThread.class

    public static void main(String[] args) {
          ...
            Looper.prepareMainLooper();
    
           ...
            Looper.loop();
    
            ...
        }
    

    简化之后,我们可以看出,在main函数中,有两处和Looper相关的代码,那么我们分别来看下
    ** Looper.class**

     public static void prepareMainLooper() {
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
        }
    
     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));
        }
     public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
        }
    
    • prepareMainLooper最终调用到的是prepare函数
    • prepare的quitAllowed参数值为false,意味着在主线程中,不允许退出
    • 在prepare函数中,我们new了一个Looper对象,放在了一个ThreadLocal的对象中保存
    • sMainLooper 是将sThreadLocal中保存的值取出,赋值给它

    ThreadLocal的简单介绍

    在整个Looper的创建以及获取的过程,其实主要跟ThreadLocal的set,get函数相关,那么我们就来看看这两个函数的源码

     public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }
    
    public T get() {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
            return setInitialValue();
        }
    
    • set函数虽然只需要传value值进来,但是其实它是获取的当前线程作为key值,将其保存,这也就说明为什么一个线程只对应来一个looper,因为他的存储的数据结构只支持一对一的关系。
    • get函数也是通过以当前线程作为key将对应的entry取出,返回给调用方

    那么以上就是mainLooper的创建的主要流程

    主线程的Looper如何开启消息轮询的呢?

    大家回顾下前面ActivityThread.main方法,我们还有一行代码没有分析,那就是Looper.loop,根据字面意思我们也能猜出,这是开始进行循环,话不多说,上代码
    Looper.class

     public static void loop() {
            final Looper me = myLooper();
        ...
            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);
                    }
                }
              ...
        }
    

    这里我们同样省略了很多源码,只分析主要流程

    • 通过myLooper()获取到当前线程的looper对象,上面也带大家一起分析过源码了,有不清楚的可以回过去看看
    • 通过拿到的looper对象去获取到当前的消息队列
    • 进入死循环
      • 不断的从队列中获取新的消息
      • 获取到消息后,msg.target.dispatchMessage(msg)

    以上就是Looper开启轮询的整个调用流程。

    下面我带大家一起看下几个问题:

    1. MessageQueue.next是如何拿到message的呢?
      MessgeQueue.class
      Message next() {
          ...
            for (;;) {
               ...
                synchronized (this) {
                  ...
                    Message prevMsg = null;
                    Message msg = mMessages;
                    ...
                    if (msg != null) {
                        if (now < msg.when) {
                           ...
                        } else {
                            // Got a message.
                            mBlocked = false;
                            if (prevMsg != null) {
                                prevMsg.next = msg.next;
                            } else {
                                mMessages = msg.next;
                            }
                            msg.next = null;
                           ...
                            msg.markInUse();
                            return msg;
                        }
                    } else {
                      ...
                    }
    
                    // Process the quit message now that all pending messages have been handled.
                    if (mQuitting) {
                        dispose();
                        return null;
                    }
    
                 ...
        }
    
    • 在next函数中也有一个死循环,当获取到我们的msg之后,跳出循环
    • next函数,就是从当前的message获取到nextMessage,并将其返回给looper

    看完之后,你一定还有很多疑问,为什么要用到死循环?当消息队列为空的时候,next函数会怎么办?等等等等,跟MessageQueue.enqueueMessgae方法一样,next函数的实现大有文章,单独为大家写一篇详细介绍,这里只作简单的流程分析。

    1. 什么情况下msg为空,跳出整个循环呢?
      这一块先跟大家简单的介绍一下,在next函数中,我们发现有return 为 null的情况,取决于一个叫mQuitting的参数,其实就是调用了MessageQueue.quit()方法,将其设置为true。但是在主线程中,大家还记得我们在Looper.prepare传入的false参数吗?它不允许主线程的MessageQueue执行quit方法,直接抛出异常。
     void quit(boolean safe) {
            if (!mQuitAllowed) {
                throw new IllegalStateException("Main thread not allowed to quit.");
            }
    
            synchronized (this) {
                if (mQuitting) {
                    return;
                }
                mQuitting = true;
    
                if (safe) {
                    removeAllFutureMessagesLocked();
                } else {
                    removeAllMessagesLocked();
                }
    
                // We can assume mPtr != 0 because mQuitting was previously false.
                nativeWake(mPtr);
            }
        }
    
    1. msg.target.dispatchMessage(msg)是怎么回调到handler.handleMessage方法的呢
      ** Message.class **
       /*package*/ Handler target;
    

    是的,正如上面我们所预料的,target其实是一个handler的对象,在Handler.enqueueMessage方法中,我们将msg与handler进行了关联,那么在Looper.loop里面,msg.target.dispatchMessage(msg),根据源码可以得知,我们将其回调给了handleMessage进行事件处理。

    /**
         * Handle system messages here.
         */
        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    

    以上就是针对Handler源码的一个简单的分析,后续会对一些细节化的设计,进行专题讲解,敬请关注。

    相关文章

      网友评论

        本文标题:Handler源码解析系列一

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