美文网首页
Handler消息通信机制总结

Handler消息通信机制总结

作者: 一线游骑兵 | 来源:发表于2019-01-04 15:04 被阅读10次

    总结一下Handler中的点

    首先说一下涉及到的类
    • Message:消息实体类
    • MessageQueue:消息队列
    • Looper:轮询器
    • Handler:消息的发送与接收者
    创建Handler机制的流程

    1. 创建Handler

        public Handler(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;
        }
    

    首先会通过Looper.myLooper();获取到当前创建Handler线程中的Looper轮询器:mLooper。如果当前线程没有,则抛出异常。因此,在非UI线程创建Handler首先要通过Looper.prepare()来创建一个当前线程的轮询器:

        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));
        }
    

    在创建完成之后,会将其放入当前线程中的ThreadLocal类中,因此每个线程都拥有当前线程私有的一套消息通信机制。
    再回到Handler构造函数中,如果mLooper已经准备就绪,则获取到该Looper中的消息队列,之后再调用Looper.loop()开启轮询。至此,消息通信机制准备完毕。
    下面来看一下loop方法:

        public static void loop() {
            final Looper me = myLooper();
    
            final MessageQueue queue = me.mQueue;   //获取当前线程中的消息队列
    
            for (;;) {      //开启死循环
                Message msg = queue.next(); // 不断的获取队头消息
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
                
                try {
                    msg.target.dispatchMessage(msg);        //消息不为空则调用将自己分发给handler
                } finally {
                }
    
                msg.recycleUnchecked();     //回收当前的消息【对象池】
            }
        }
    

    loop方法中做了这么几件事儿:

    • 获取到当前线程的消息队列
    • 开启死循环一直从队列取消息
    • 有消息的话就将消息分发给handler

    到此为止,Handler消息通信机制准备完毕,可以使用了。

    总结一下,想要使用Handler跨线程通信需要做到:

    1. 首先在Handler创建之前,需要在同一线程调用Looper.prepare()创建当前线程轮询器和消息队列,创建完毕后会将该线程的Looper轮询器存放到线程私有的ThreadLocal中。
    2. 创建Handler,在Handler的构造函数中会通过ThreadLocal来获取当前线程的轮询器Looper,为空则抛异常,并获取Looper中的消息队列。
    3. 调用Looper.loop()开启一个死循环不断从队列中取出消息分发给Handler。
    那么问题来了:
    为什么平时在主线程使用Handler并不需要调用Looper.prepare()Looper.loop()

    因为在创建UI线程的时候,在UI线程的main方法里边,系统已经为我们做好了UI线程中的handler准备操作,因为四大组件的生命周期也是通过Handler来进行调度的。先来看下main中的操作:

       public static void main(String[] args) {
         
            Looper.prepareMainLooper();
    
            ActivityThread thread = new ActivityThread();
            thread.attach(false, startSeq);
    
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
          
            Looper.loop();
        }
    
    • 首先调用了Looper.prepareMainLooper();来生成UI线程中的Looper。之所以和prepare()不一样是因为将在该线程生成的Looper存放到了Looper对象中。之后在Looper对象中使用sMainLooper变量来存起来,之后用户可以通过new Handler(Looper.getMainLooper());来获取到主线程中的轮询器和消息队列并通过handler向主线程发送消息,从而达到了在子线程更新UI的操作。
    消息的发送与分发过程

    发送消息有两种方式:

    1. handler.sendMessage(msg);
        public final boolean sendMessage(Message msg){
            return sendMessageDelayed(msg, 0);
        }
    

    2.handler.post(runable);

        public final boolean post(Runnable r)  {
           return  sendMessageDelayed(getPostMessage(r), 0);
        }
    
        private static Message getPostMessage(Runnable r) {
            Message m = Message.obtain();
            m.callback = r;
            return m;
        }
    

    可以看到两种法师最终走的都是同一个方法。只不过第二种方法会将Runnable赋值给Message中的callback变量。
    sendMessageDelayed最终会调用下边的方法:

        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
            MessageQueue queue = mQueue;
            if (queue == null) {
                return false;
            }
            return enqueueMessage(queue, msg, uptimeMillis);
        }
    

    将消息入队:

        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,后边在从消息队列中分发消息也是通过这个target进行分发的。因此,消息的发送与接收处理最终转了一圈又回到了Handler中。

    接下来看如何把消息加入队列(去除一些空判断代码):

        boolean enqueueMessage(Message msg, long when) {
            synchronized (this) {
             
                Message p = mMessages;
               
                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 {
                   
                    Message prev;
                    for (;;) {
                        prev = p;
                        p = p.next;
                        if (p == null || when < p.when) {
                            break;
                        }                   
                    }
                    msg.next = p; // invariant: p == prev.next
                    prev.next = msg;
                }
    
            }
            return true;
        }
    
    1. 如果队列为空(mMessaegs队头==null),则走进if语句,新建一个head。将mMessages指向队头。
    2. 否则,开启一个for循环,一直循环到队列最后一个消息,然后将最后一个元素的next指向新入队的msg。

    至此,一个消息就已经添加到消息队列中去了。下面看一下在loop的for循环中是如何从队列取消息的:

        Message next() {
        
            for (;;) {
    
                nativePollOnce(ptr, nextPollTimeoutMillis);     //处理native层的消息通信
    
                synchronized (this) {
                    final long now = SystemClock.uptimeMillis();
                    Message prevMsg = null;
                    Message msg = mMessages;
                    if (msg != null && msg.target == null) {    //循环遍历链表获取队尾
                        do {
                            prevMsg = msg;
                            msg = msg.next;
                        } while (msg != null && !msg.isAsynchronous());
                    }
                    if (msg != null) {
                        if (now < msg.when) {   //延时处理
                            // Next message is not ready.  Set a timeout to wake up when it is ready.
                            nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                        } else {
                            // Got a message.   获取一个可用的msg,
                            if (prevMsg != null) {  //将该msg出队
                                prevMsg.next = msg.next;
                            } else {
                                mMessages = msg.next;
                            }
                            msg.next = null;    //出队操作
                            return msg;     //返回消息
                        }
                    } else {
                        // No more messages.
                        nextPollTimeoutMillis = -1;
                    }
    
                }
            }
        }
    

    从表头开始遍历可用的msg并出队然后返回。
    在loop循环中获取到队列中的msg之后会通过msg.target.dispatchMessage(msg);最终将msg分发给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);
            }
        }
    
        private static void handleCallback(Message message) {
            message.callback.run();
        }
    
        /**
         * Subclasses must implement this to receive messages.
         */
        public void handleMessage(Message msg) {
        }
    

    最终会将消息传给handleMessage供实现类处理,或者直接运行post的runnable。

    特别说明:
    1. Message的创建使用了对象池【链表形式,同EventBus中的PendingPost与PendingPostQueue实现】,因此创建Message最好通过Message.obtain()获取。用完之后会自动recycler入池。
        public static Message obtain() {
            synchronized (sPoolSync) {
                if (sPool != null) {
                    Message m = sPool;
                    sPool = m.next;
                    m.next = null;
                    m.flags = 0; // clear in-use flag
                    sPoolSize--;
                    return m;
                }
            }
            return new Message();
        }
    
    1. 在Handler所在页面退出时最好调用一下:handler.removeCallbacksAndMessages(null)来移除已发送未收取或执行的runnable和msg。
    完。

    相关文章

      网友评论

          本文标题:Handler消息通信机制总结

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