美文网首页
Handler(二) - 如何发送消息

Handler(二) - 如何发送消息

作者: 世道无情 | 来源:发表于2019-01-27 16:27 被阅读0次

1. 如何发送消息


一般都会:new Message对象,然后调用 msg.args1或者msg.setData(bundler)设置数据,然后调用 handler.sendMessage(msg)发送消息即可,实例代码如下:

        new Thread(new Runnable() {
            @Override
            public void run() {
                Message msg = new Message() ;
                msg.arg1 = 1 ;
                Bundle bundle = new Bundle() ;
                bundle.putString("data" , "data");
                msg.setData(bundle);
                handler1.sendMessage(msg);
            }
        }).start();

这里会有几个问题:
1>:handler 把 msg 消息发送到哪里了?
2>:为什么可以在 handler.handleMessage(msg)方法中可以获取到发送的那条消息呢?
下边通过分析源码,来解释这两个问题;

需要知道的是:handler中发送消息的方法有很多,除了sendMessageAtFrontOfQueue()以外,其他发送消息的方法最终都会调用到sendMessageAtTime()方法中;

2. 源码分析


比如从 sendMessage(msg)中进入sendMessageAtTime()方法:

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

  /**
   *  msg:是sendMessage(msg)中发送的 msg对象;
   *  uptimeMillis:发送消息的时间   
   */
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;
        }

        // 把 msg 和 uptimeMillis 传递给 MessageQueue 的 enqueueMessage()方法中
        return enqueueMessage(queue, msg, uptimeMillis);
    }

MessageQueue: 是消息队列,用于把所有接收到的消息以队列的形式进行排列,提供入队和出队的方法,MessageQueue是在Looper类的 构造方法中 创建的,所以 一个Looper 对应一个 MessageQueue;

enqueueMessage():入队方法,该方法源码如下,这个方法是Handler类中的:

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

        // 由于这个方法是 handler 中的,所以 msg.target 就是 handler
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }

        // 这里调用 MessageQueue中的 enqueueMessage() 方法
        return queue.enqueueMessage(msg, uptimeMillis);
    }
boolean enqueueMessage(Message msg, long when) {
        // ...省略一些代码
        synchronized (this) {
            // ...省略一些代码

            // 时间,msg被加入到MessageQueue的时间 
            msg.when = when;
            // 当前待处理的消息:MessageQueue没有用集合保存所有消息,而是用 mMessages表示当前待处理的消息
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // 根据时间的顺序调用 msg.next,给每一个消息指定它的下一个消息是什么
                msg.next = p;
                // 此时,就把 mMessages赋值为 新入队的 这条消息,然后把这条消息的 next 指定为刚才的 
                // mMessages ,到这里就完成了 添加消息到队列头部的操作
                mMessages = msg;
            } else {
                // ... 省略一些代码
            }
        }
        return true;
    }

这里要知道:MessageQueue没有用 集合 把所有消息都保存起来,而是用 mMessages表示当前待处理的消息,

所谓入队就是:

把所有消息按时间进行排序,这个时间就是上边的 uptimeMillis,就是根据时间的顺序调用 msg.next,用于给每个消息指定它的下一个消息是什么

出队方法在Looper.loop()中:
public static void loop() {
        final Looper me = myLooper();
        }
        final MessageQueue queue = me.mQueue;

        // 这里很重要:在Looper.loop()方法中有一个死循环,会不断的调用消息队列 MessageQueue.next()方法,
        // 只要 消息队列中有待处理的消息,就会通过 queue.next()取出待处理的消息 msg,
        // 然后 交给 handler.dispatchMessage() 来处理
        for (;;) {
            // 这个就是消息队列MessageQueue的 出队方法
            Message msg = queue.next(); // might block
            if (msg == null) {
                return;
            }

            try {
                //  从消息队列MessageQueue中 出队的消息 msg:只要上边有消息出队,就把它传递到
                //   msg.target 的 dispatchMessage()方法中,由上边的 Handler的 enqueueMessage()
                //   方法,可知:msg.target就是 handler,也就是说这里调用的就是
                //   handler的 dispatchMessage()方法;
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            msg.recycleUnchecked();
        }
    }

出队方法queue.next():
如果消息队列MessageQueue中存在待处理消息 mMessages,就让这个消息出队,然后让下一个消息 成为mMessages;
如果消息队列MessageQueue中不存在 待处理消息 mMessages,就进入阻塞状态,直到有新的 消息入队;

handler的 dispatchMessage()方法:

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            // 从这里可知:
            // 如果mCallback 不为空,就调用mCallback 的 handleMessage()方法;
            // 否则,就调用 handler的 handleMessage(msg)方法,然后把 msg传递过去
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

到此就知道:在 handler的 handleMessage(msg)方法中就可以去取出 一开始sendMessage(msg)中的msg了;

3. 为什么用异步消息处理方式可以更新UI?


因为 handler 依赖于创建 handler 对象所在的线程,比如在主线程中创建handler对象,在子线程中不能直接更新UI,需要通过一系列的 发送消息、入队、出队等操作,最后调用到 handler.handleMessage(msg) 方法,此时 handleMessage()方法已经在主线程中,所以就可以在 handleMessage() 方法中更新UI

4. 异步消息处理流程图如下


图片.png

1>:创建handler对象,然后调用 handler.sendMessage(msg)发送消息到 消息队列MessageQueue中;
2>:消息队列MessageQueue中的消息,是按照时间顺序排序的,然后根据时间调用 msg.next,给每个消息指定它的下一个消息是什么,此时就把 mMessages赋值为 新入队的这条消息,然后把 这条消息的 next 指定为刚才的 mMessages,此时完成 添加消息到对头的操作;
3>:然后在 Looper.loop()方法中:有一个 死循环,for(; ;),在这个死循环中会不断的调用 MessageQueue.next()方法,表示不断的从 消息队列中取出消息:
如果有就取出,然后返回 msg对象,然后调用 handler.dispatchMessage(msg)处理,然后调用 handler.handleMessage(msg)处理消息;
如果没有消息:就处于阻塞状态,直到有新的消息入队;

相关文章

网友评论

      本文标题:Handler(二) - 如何发送消息

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