美文网首页
Android-Handler消息机制

Android-Handler消息机制

作者: 小巨人Vea | 来源:发表于2020-01-08 14:10 被阅读0次

    Handler在Android中负责调度消息并将来某个时段处理消息。Android有大量的消息驱动方式来进行交互,比如四大组件的的启动过程的交互,都离不开消息机制。

    消息机制涉及MessageQueue/Message/Looper/Handler这4个类。

    Message:消息分为硬件产生的消息(如按钮、触摸)和软件生成的消息;

    MessageQueue:消息队列的主要功能向消息池投递消息和取走消息池的消息;

    Handler:消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage);

    Looper:不断循环执行(Looper.loop),按分发机制将消息分发给目标处理者。

    梳理过程从handler构造函数开始,然后产生一条消息通过sendMessage向下分发。之后又是如何被调度,最后被怎样处理。

     public Handler(@Nullable Callback callback, boolean async) {   
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    

    这里是通过Looper中的myLooper方法来获得Looper实例的,如果Looper为null的话就会抛异常,抛出的异常内容翻译过来就是无法在未调用 Looper.prepare()的线程内创建handler,new handler的时候没有报错那么就一定之前就调用了 Looper.Prepare(),在哪里创建的呢?

    答案在 ActivityThread.main() 中。

    ActivityThread 的 main()方法就是整个APP的入口,也就是我们通常所讲的主线程, UI线程。但他实际上并不是一个线程,ActivityThread 并没有继承 Thread 类,我们可以把他理解为主线程的管理者,负责管理主线程的执行,调度等操作,看下main方法的源码。

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

    两个关键方法 Looper.prepareMainLooper()和Looper.loop(),loop方法后面再分析,继续看prepareMainLooper的方法内部如下:

      
       // 1
        public static void prepareMainLooper() {
            // 2
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
        }
        
       // 2
        private static void prepare(boolean quitAllowed) {
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            // 3
            sThreadLocal.set(new Looper(quitAllowed));
        }
     
      // 3
      private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
    
    

    到1⃣️这里就解决了,为什么我们在主线程中使用Handler之前没有调用Looper.prepare方法的问题了,就是在这里调用了prepare 方法。
    2⃣️prepare方法内部把 Looper 放到ThreadLocal中与当前线程绑定,保证一个线程只有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
    3⃣️在Looper的构造函数中始化MessageQueue

    所以初始化Handler之前Looper 和 MessageQueue就已经初始化了,并把Looper放到ThreadLocal中。ThreadLocal有个特点是你set进去的值是以当前所处的线程为key,value就是你set的值。源码如下:

    public void set(T value) {
           Thread t = Thread.currentThread();
           ThreadLocalMap map = getMap(t);
           if (map != null)
               map.set(this, value);
           else
               createMap(t, value);
       }
    

    Handler的sendMessage方法都做了什么

    先看一下sendMessage的流程图:


    sendMessage

    简单过一遍,sendXXX 这些方式最终还是会调用到 enqueueMessage 这个方法上来的逻辑最后调用queue.enqueueMessage 添加消息到消息队列MessageQueue中,下面就是消息的入队逻辑

        boolean enqueueMessage(Message msg, long when) {
                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;
                }
            }
            return true;
        }
    

    消息入队顺序是按照 Message 触发时间 long when入队的,有新消息加入时,会循环遍历当前消息队列,对比消息触发时间,直到找到当前消息合适的插入位置,以此来保证所有消息的触发时间顺序。即 MessageQueue 添加消息到消息队列中。

    所以消息的存储与管理MessageQueue 来负责

    谁负责把消息分发出去

    Handler把Message交给MessageQueue并存储起来 ,那么谁来负责把我这里的消息分发出去并且告诉主线程的人(Handler)呢?答案就是 Looper 了!


    Looper

    之前分析的ActivityThread.main()中的Looper.loop还没有讲到我们接着跟进去看一下源码

    public static void loop() {
        final Looper me = myLooper();
        final MessageQueue queue = me.mQueue;
        for (;;) {
           // 循环调用 MessageQueue 的 next 方法获取消息
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            try {
                msg.target.dispatchMessage(msg);
            }
        }
    }
    

    loop()不断从MessageQueue中取消息,把消息交给target(handler)的dispatchMessage方法处理。下面是dispatchMessage

    public void dispatchMessage(@NonNull Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    

    可以看到dispatchMessage最后调用了handleMessage方法,在源码里面是个空实现。需要我们在创建handler的时候复写,根据不同的message处理不同的业务逻辑。

    整个Handler的消息分发调度流程就讲完了,为了增强记忆,我再把整个流程拼成一块如下图:


    Handler消息机制

    相关文章

      网友评论

          本文标题:Android-Handler消息机制

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