Handler的sendMessage和postDelay的区别

3,448 阅读4分钟

原来一直觉得源码都看过了,应该也差不多了,今天用到了postDelay,然后问自己postDelay和sendMessage有什么区别,是如何处理postDelay的Runnable的,然后就没有然后了;
如果你也有这个疑问那就跟我往下一起看,如果你知道,请看我说的是不是你想的,或者我说的有哪些不足之处,

1、消息入队

1.postDelayed消息入队

我们先往里追源码顺便再熟悉一下handler的知识点

    public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
	//这里我们看到postDelayed还是调用自身的sendMessageDelayed,我们接着去看sendMessageDelayed
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

这里我们看到sendMessageDelayed其实也是接收一个Message,那我们回到postDelayed看看这个Message如何处理Runnable的

    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

这里我们看到是把Runnable放到了Message.callback中,然后把Message传递到sendMessageDelayed中,这样就通了

    private static Message getPostMessage(Runnable r) {
	//Message.obtain() 从全局池返回一个新的Message实例。使我们在许多情况下避免分配新对象
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

现在我们接着看sendMessageAtTime

    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    //如果mQueue为空说明没有调用prepare,这个我们下面再说
        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);
    }

2.sendMessage消息入队

    public final boolean sendMessage(@NonNull Message msg) {
    //在此我们发现postDelayed()只是把Runnable封装了一下,走的逻辑还是一样的,
        return sendMessageDelayed(msg, 0);
    }

到此我们看到消息入队已经完毕,我们接着去看消费。

2、消费

我直接进Looper.loop(),顺便简单梳理一下流程
这里补一个小知识点,
线程和 Handler Looper MessageQueue 的关系是一个线程对应一个 Looper 对应一个 MessageQueue 对应多个 Handler,既然是一个MessageQueue对应多个Handler,会不会Handler_A发消息Handler_B会同时接到,答案是不会的,下面会有说。
1.获取当前线程对应的Looper
2.获取对应的MessageQueue
3.拿到msg消息
4.msg消费

    public static void loop() {
    //1.获取当前线程对应的Looper
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //2.获取对应的MessageQueue
        final MessageQueue queue = me.mQueue;
	...
        boolean slowDeliveryDetected = false;

        for (;;) {
        3.拿到msg消息
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
	...
            Object token = null;
            if (observer != null) {
                token = observer.messageDispatchStarting();
            }
            long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
            try {
            4.msg消费
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {}
	...
            msg.recycleUnchecked();
        }
    }

说了这么久马上就到重点了不要着急,现在我们去看看第4步target是什么,

public final class Message implements Parcelable {
	//在这里能看到Message是持有Handler的引用,也就是说不存在Handler_A发消息Handler_B会同时接到的问题
	@UnsupportedAppUsage
	/*package*/ Handler target;
}

我们看看target是在哪赋的值

public class Handler {
	private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {
		msg.target = this;
	}
}

现在我们回到第4步看msg.target.dispatchMessage(msg);是不是就明朗了,现在我们进Handler.dispatchMessage()看做什么处理了

    public void dispatchMessage(@NonNull Message msg) {
    //在这里,如果msg.callback != null执行handleCallback(),
        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();
    }

总结

postDelay()方法会通过getPostMessage()在消息入队之前封装成一个Message,在消费那里通过判断msg.callback是否为空来判断是否执行Runnable.run()。
其次,检查mCallback是否为null,不为null就调用mCallback的handleMessage方法来处理消息。Handler.CallBack是个接口,定义如下:


public class Handler {
   //Callback interface you can use when instantiating a Handler to avoid having to implement your own subclass of Handler
   
   public interface Callback {
   /**
    * @param msg A {@link android.os.Message Message} object
    * @return True if no further handling is desired
    */
   boolean handleMessage(@NonNull Message msg);
   }
}

最后调用Handler的handleMessage方法来处理消息。Handler消息处理的过程可以归纳为一个流程图。

image.png Handler五花八门的post/send api们本质上无差别。只是为了让使用者在简单的情况下避免手动封装Message,只需提供一个Runnable即可。
只是一些个人的总结,如果有不通的地方欢迎大家交流,错别字也请指出来。