美文网首页
Android 从源码角度理解Handler、Looper、Me

Android 从源码角度理解Handler、Looper、Me

作者: 裴庆圣 | 来源:发表于2018-08-14 17:26 被阅读0次

    为什么?

    多问为什么是解决并理解问题的最好的办法,Handler是Android SDK中的类,Android为什么要引入Handler、Looper、MessageQueue呢?

    线程间通讯,消息队列处理

    经常使用的场景有通过Handler更新UI,貌似我们理解中更新UI只能在主线程
    这句话好像是对的,其实更准确的说法是更新UI只能在UI线程,即要更新的UI所在ViewRoot创建时候的线程,如果当前ViewRoot是在子线程创建的,更新ViewRoot里面的UI必须在这个子线程中更新,而我们常见的ViewRoot创建都是在主线程,所以把常见的结果当成标准结果是万万不可的。
    为什么采用消息队列的形式呢?消息队列的方式可以解决线程堵塞数据混乱等问题。

    言归正传

    Handler

    Handler是用来发送和处理消息的
    Handler对象的创建需要绑定一个指定线程的Looper, 即Handler是和线程相关的,如一个Handler绑定的是一个子线程中的Looper,那么这个Handler就不能用来更新UI线程(主线程)。

    Looper

    Looper是通过for (;;)循环来不停的把MessageQueue中的消息交给Handler处理

    MessageQueue

    用来存储Message的消息队列

    源码分析(类分析)

    Handler的构造函数中只有2个基本构造函数
    1.未指定Looper

    public Handler(Callback callback, boolean async) {
            //省略部分代码
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    

    2.指定Looper

     public Handler(Looper looper, Callback callback, boolean async) {
            mLooper = looper;
            mQueue = looper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    

    其中未指定Looper通过mLooper = Looper.myLooper(); 来获取当前线程中的Looper
    让我们来看看Looper.myLooper();究竟做了哪些操作 ,打开Looper源码

    public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
        }
    

    额。。。sThreadLocal是什么鬼,怎么通过他返回一个Looper 继续查看

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    

    原来是一个ThreadLocalThreadLocal用于保存某个线程共享变量,当前即为保存线程中Looper变量,通过sThreadLocal来获取当前线程的Looper,在过sThreadLocal没有进行set()的时候sThreadLocal.get() 为空,为空的时候

     if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
    

    会提示RuntimeException 提示我们在创建对象之前必须先执行Looper.prepare()
    那为什么我们经常在使用Handler更新UI的时候没有执行过呢,其实并不是没有执行,只不过并不需要我们来执行,在ActivityThreadmain方法中已经执行过Looper.prepareMainLooper();所以不需要我们执行,系统已经帮我们执行过了
    而在子线程中创建Handler 或指定的子线程中的Looper,必须我们手动执行Looper.prepare()不然就会报RuntimeException异常信息
    继续查看Looper.prepare()执行了哪些操作

    public static void prepare() {
            prepare(true);
        }
    
    private static void prepare(boolean quitAllowed) {
            //如果已经存在了Looper也会报异常 所以Looper.prepare()只能必须执行1次
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            //保存在sThreadLocal中
            sThreadLocal.set(new Looper(quitAllowed));
        }
    

    继续看new Looper(quitAllowed)的处理

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

    创建MessageQueue对象 绑定当前线程

    小结

    自此三者的关系明朗了起来
    Handler绑定了Looper,Looper创建了一个MessageQueue对象
    Handler拥有Looper、MessageQueue成员变量

    源码分析(如何发处理消息)

    通过层层跳转,发送消息都会执行到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(queue, msg, uptimeMillis)方法

     private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            //设置msg的target为当前Handler
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    这里主要就是设置msg的target为当前Handler然后调用MessageQueue的enqueueMessage(msg, uptimeMillis)方法把此msg存储到消息队列中
    到了这里好像结束了,源码分析完了,等等还没消息处理呢,怎么只有发送,没有接收处理 自此Looper.loop()方法登场了

    public static void loop() {
            //省略部分代码
            for (;;) {
            Message msg = queue.next(); // might block
               //省略部分代码
                try {
                    //执行msg的target(Handler)的dispatchMessage方法
                    msg.target.dispatchMessage(msg);
                    end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
               //省略部分代码
            }
        }
    

    原来for循环不停的取出msg执行msg对于的Handler中的dispatchMessage(msg)方法

     public void dispatchMessage(Message msg) {
            //如果msg创建的时候设置了callback则会执行callback的run方法
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                //如果在创建Handler的时候设置了callback则会执行callback的handleMessage方法
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                //都没有设置则会执行Handler的handleMessage方法
                handleMessage(msg);
            }
        }
    

    至此 Handler的发送与消息处理整个流程就解析完毕

    总结

    线程中使用Handler步骤
    1.Looper.prepare() 准备Looper(主线程已经在ActivityThread中执行);
    2.new Handler() 创建Handler(注意指定的Looper线程或创建对象所在的线程)
    3.Looper.loop()启动Looper 循环取出MessageQueue中的消息交给Handler处理(主线程已经在ActivityThread中执行)

    发送消息流程
    1.Handler.sendMessageAtTime() 定时发送消息
    2.MessageQueue.enqueueMessage()存储消息至消息队列
    3.Handler.dispatchMessage(msg);消息的分发处理

    相关文章

      网友评论

          本文标题:Android 从源码角度理解Handler、Looper、Me

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