美文网首页Android学习
Android Handler之原理解析

Android Handler之原理解析

作者: 王魔王 | 来源:发表于2019-05-15 14:37 被阅读0次

    续上篇
    Android Handler之从主线程往子线程发消息(一)

    简单回顾一下Handler机制中几个对象的主要作用

    Handler机制中最重要的四个对象
    Handler:负责发送消息及处理消息
    Looper:复制不断的从消息队列中取出消息,并且给发送本条消息的Handler
    MessageQueue:负责存储消息
    Message:消息本身,负责携带数据
    那么,一个消息从发送出去,到回到Handler自己身上,这个过程具体是怎样的?
    这个就不得不去看源码了
    我们从Android Handler之从主线程往子线程发消息(一)中的
    四、怎么从主线程发送消息到子线程?(虽然这种应用场景很少)的示例代码看起

    Thread thread = new Thread(){
                @Override
                public void run() {
                    super.run();
                    //初始化Looper,一定要写在Handler初始化之前
                    Looper.prepare();
                    //在子线程内部初始化handler即可,发送消息的代码可在主线程任意地方发送
                    handler=new Handler(){
                        @Override
                        public void handleMessage(Message msg) {
                            super.handleMessage(msg);
                          //所有的事情处理完成后要退出looper,即终止Looper循环
                            //这两个方法都可以,有关这两个方法的区别自行寻找答案
                            handler.getLooper().quit();
                            handler.getLooper().quitSafely();
                        }
                    };
                  
                    //启动Looper循环,否则Handler无法收到消息
                    Looper.loop();
                }
            };
            thread.start();
        //在主线程中发送消息
        handler.sendMessage();
    

    选择这个蹩脚的demo是为了让大家更好的理解Handler机制,耐心看下去,最后你心中所有的疑问都会有答案

    一、先来解释第一行代码
    Looper.prepare();

    这个很好解释,只要查看Handler的构造方法即可

    //空参的构造方法,这个方法调用了两个参数的构造方法
      public Handler() {
            this(null, false);
        }
    
    //两个参数的构造方法
    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;
        }
    

    看到了没有,Handler的构造方法中会验证Looper,如果Looper为空,那么会抛出空指针异常

    如果你足够细心,你会发现,Handler在构造方法中还做了一件事,
    将自己的一个全局消息队列对象(mQueue)指向了Looper中的消息队列
    即构造方法中的这行代码
    mQueue = mLooper.mQueue;
    先记住,有用

    二、第二行代码是初始化了Hanlder并且重写HandleMessage()方法

    这个没什么好解释的

    三、我们调用了Looper.loop()方法,这个方法的具体原理随后解释

    四、handler.sendMessage(message)的主要作用

    最接近我们使用的就是handler.sendMessage(message);这行代码了,那么我们从这行代码看起,看看这行代码之后发生了什么,为了方便大家看到,我把代码执行流程画了出来

    sendMessage()之后代码执行流程
    红线画出来的代表代码是哪个类的,可以看到,我们sendMessage()之后代码通过图中所示的几个方法,最终执行到了MessageQueue的enqueueMessage()方法。也就是说,接下来我们要看的就是MessageQueue中的方法了。
    上源码
    boolean enqueueMessage(Message msg, long when) {
            if (msg.target == null) {
                throw new IllegalArgumentException("Message must have a target.");
            }
            synchronized (this) {
                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                boolean needWake;
                if (p == null || when == 0 || when < p.when) {
                    // New head, wake up the event queue if blocked.
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked;
                } else {
                    needWake = mBlocked && p.target == null && msg.isAsynchronous();
                    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;
                }
                // We can assume mPtr != 0 because mQuitting is false.
                if (needWake) {
                    nativeWake(mPtr);
                }
            }
            return true;
        }
    

    为了方便阅读,我删减了部分代码
    可以看到,MessageQueue是一个单向列表结构,而MessageQueue的enqueueMessage()方法主要做的事情就是将Handler发送过来的Message插入到列表中。
    也就是说,当我们调用handler.senMessage()方法的时候,最终的结果只是将这个消息插入到了消息队列中
    上面的流程图中有一个问题:
    最后一行代码queue.enqueueMessage()中的queue对象是什么时候初始化的?
    还记得本篇博客在解释Looper.prepare()方法部分的最后一段话吗?
    不记得了就往上翻一翻

    发送消息的工作已经完成,那么Looper是什么时候取的消息,取出来消息又是怎么送回给Handler的呢?现在我们就不得不看Looper.loop()方法了

    Looper.loop()方法

    public static void loop() {
            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);
                } 
            }
        }
    

    同样的,为了降低阅读难度,我删掉了大部分的代码,只留下了这么几行核心代码,现在我们来看这几行代码的逻辑

    • 这是一个死循环
    • 这个循环的目的是从MessageQueue中取出消息
    • 取消息的方法是MessageQueue.next()方法
    • 取出消息后调用message.target对象的dispatchMessage()方法分发消息
    • 循环跳出的条件是MessageQueue.next()方法返回了null

    不过,看到这里我们应该自然会想到,message.target.是哪个对象?
    该对象的dispatchMessage()方法都做了什么操作?
    带着疑问,我们进入Message类中去寻找target

    public final class Message implements Parcelable {
     /*package*/ int flags;
    
        /*package*/ long when;
    
        /*package*/ Bundle data;
    
        /*package*/ Handler target;
    
        /*package*/ Runnable callback;
    
        // sometimes we store linked lists of these things
        /*package*/ Message next;
    }
    

    看到了吧,Message类中有一个成员变量 target,而这个target是一个Handler,也就是说,在Looper从MessageQueue中取出Message之后,调用了Handler的dispatchMessage()方法。
    这里我们不禁要问,这个target指向了哪个Handler
    带着这个疑问,我翻遍了Message类,也没有看到我想要的答案。此时我就想,既然Handler发送了消息就能接收到消息,那么会不会是在发送消息的途中发生了什么细节是我不知道的,那么我只好仔细看发送消息过程中的代码,终于让我发现了这个

    //Handler的方法
     private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    在这个方法的第一行代码就是,将message的target属性赋值为发送message的handler自身。如果读者忘记了这个方法的调用时机,请往上翻翻,查看一下第四部分sendMeeage()中的流程图

    也就是说,Looper取出消息后,调用了发送消息的Handler的dispatchMessage()方法,并且将message本身作为参数传了回去。到此时,代码的执行逻辑又回到了Handler中。

    接着看handler的dispatchMessage()方法

    /**
        *handler的方法
         * 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);
            }
        }
    

    看到这里面一个我们非常熟悉到方法了没有?---handleMessage()方法,对,这个方法就是我们经常要重写的handleMessage()方法,也是我们处理消息时候的逻辑。
    到这里,Handler机制工作的主要流程就完成了。
    再来一个系统的工作示意图

    Handler工作示意图
    如果读者认真看了博客,那么到这里就应该对Handler的基本工作流程比较清晰了。我也在前面详细讲解了流程图中的方法调用过程。不过还有一些问题我们没有处理
    一个线程中最多可以有几个Looper和几个MessageQueue?
    我们来看Looper的构造方法即初始化方法
    //Looper暴露出的静态初始化方法
    //这个方法会调用下面的私有静态方法
      public static void prepare() {
            prepare(true);
        }
    //Looper私有的静态方法
       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));
        }
    //私有的构造方法,禁止外界调用
      private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
    
    //从sThreadLocal中获取一个Looper
     public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
        }
    

    解释一下上面三个方法

    • 我们只能通过Looper.prepare()方法去初始化一个Looper
    • Looper.prepare(boolean)方法的逻辑是一个线程中只能有一个Looper对象,否则在第二次尝试初始化Looper的时候,就会抛出异常。
    • 以线程为单位存储Looper的主要逻辑是通过ThreadLocal实现的
    • 私有的构造方法,禁止外界任意new出一个Looper

    通过这段逻辑我们可以看出,
    一个线程中最多有一个Looper
    接着看Looper的构造方法,里面有一行代码
    mQueue = new MessageQueue(quitAllowed);
    是的,我们的MessageQueue是随着Looper的初始化而初始化的。那么,MessageQueue能不能随意的被new出来呢?

    //MessageQueue的构造方法
     MessageQueue(boolean quitAllowed) {
            mQuitAllowed = quitAllowed;
            mPtr = nativeInit();
        }
    

    可以看到,MessageQuede的构造方法是default的,也就是说,只有跟MessageQueue同一个包下才可以实例化MessageQueue,换句话说,我们用户是无法直接new一个MessageQueue对象出来的。而因为Looper在一个线程中只能有一个,从而导致MessageQueue也只能有一个

    相关文章

      网友评论

        本文标题:Android Handler之原理解析

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