重点关注
Handler 的 post(Runnable) 与 sendMessage(Message msg) 有什么区别
样例:
public class HandlerActivity extends ActivityBase{ ActivityHandlerBinding binding ; private Handler handler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(@NonNull Message msg) { switch (msg.what){ case 1: binding.tvTotle.setText(String.format("哈哈哈哈考了%d",msg.arg1)); break; } return false; } }); @Override protected void onCreate(@Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityHandlerBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); binding.btnSendMessage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Message msg = new Message(); msg.what=1; msg.arg1=100; handler.sendMessage(msg); } }); binding.btnPost.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { handler.post(new Runnable() { @Override public void run() { binding.tvTotle.setText(String.format("哈哈哈哈考了%d",80)); } }); } }); } }
Handler.post()
public final boolean post(@NonNull Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
然后调用 getPostMessage() 传入 Runnable ,咱看看这个是干嘛的?
Handler.getPostMessage()
private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
在这个方法中你会发现getPostMessage()会将 Runnable 赋值到 Message 的 callback 变量中,返回一个Message。并调用 sendMessageDelayed 方法。
Handler.sendMessage()
public final boolean sendMessage(@NonNull Message msg) { return sendMessageDelayed(msg, 0); }
对比发现他们都调用 sendMessageDelayed() ,只不过生成的Message方式不同。 sendMessageDelayed() 上文已经讲到了就不多描述了。可以向上翻翻。
可以看出,经过几层调用之后,sendMessageDelayed() 最终会调用 enqueueMessage() 方法将 Message 插入到消息队列 MessageQueue 中。而这个消息队列就是我们刚才分析的在 ActivityThread 的 main 方法中通过 Looper 创建的 MessageQueue。
Looper 通过 loop() 方法从 MessageQueue 中取出 Message ,Message.target(Handrle) 会调用 dispatchMessage 方法进行处理。下面咱再看看它的源码。
Handrle.dispatchMessage()
/** * Handle system messages here. * 在这里处理系统消息。 */ public void dispatchMessage(@NonNull Message msg) { //注释1 if (msg.callback != null) { handleCallback(msg); } else { //注释2 if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
咱先看看handleCallback方法
Handrle.handleCallback()
private static void handleCallback(Message message) { message.callback.run(); }
很明显,dispatchMessage 分两种情况:
注释1:msg.callback != null,一般为通过 post(Runnabl) 方式,会直接执行 Runnable 的 run 方法。因此这里的 Runnable 实际上就是一个回调接口,跟线程 Thread 没有任何关系。
注释2:msg.callback == null,这种一般为 sendMessage 的方式,则会调用 Handler 的 hanlerMessage 方法进行处理。
Looper.loop() 为什么不会阻塞主线程
刚才我们了解了,Looper 中的 loop 方法实际上是一个死循环。但是我们的 UI 线程却并没有被阻塞,反而还能够进行各种手势操作,这是为什么呢?在 MessageQueue 的 next 方法中.
MessageQueue.next()
Message next() { // mPtr :used by native code final long ptr = mPtr; if (ptr == 0) { return null; } //-1:仅在第一次迭代期间 int pendingIdleHandlerCount = -1; //下一个Poll超时时间 int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { //将当前线程中挂起的所有 Binder 命令刷新到内核驱动程序。 //在执行可能会阻塞很长时间的操作之前调用这会很有用, //以确保任何挂起的对象引用都已被释放,以防止进程持有对象的时间超过它所需的时间。 Binder.flushPendingCommands(); } //注释:重点来了。 nativePollOnce(ptr, nextPollTimeoutMillis); ... } }
MessageQueue.nativePollOnce()
/*non-static for callbacks*/ private native void nativePollOnce(long ptr, int timeoutMillis);
nativePollOnce 方法是一个 native 方法,当调用此 native 方法时,主线程会释放 CPU 资源进入休眠状态,直到下条消息到达或者有事务发生,通过往 pipe 管道写端写入数据来唤醒主线程工作,这里采用的 epoll 机制。下面是关于 nativePollOnce 的部分分析,参考了nativePollOnce函数分析,感兴趣的可以去自己看看,有点懵。等我再努力努力再来看这些吧。
epoll机制:提供了Linux平台上最高效的I/O复用机制。 从调用方法上看,epoll的用法和select/poll非常类似,其主要作用就是I/O复用,即在一个地方等待多个文件句柄的I/O事件。
nativePollOnce的实现函数是android_os_MessageQueue_nativePollOnce。
android_os_MessageQueue_nativePollOnce
frameworks/base/core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, jlong ptr, jint timeoutMillis) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); //取出NativeMessageQueue对象,并调用它的pollOnce nativeMessageQueue->pollOnce(env, obj, timeoutMillis); }
pollOnce()
frameworks/base/core/jni/android_os_MessageQueue.cpp
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) { mPollEnv = env; mPollObj = pollObj; //重任传递到Looper的pollOnce函数 mLooper->pollOnce(timeoutMillis); mPollObj = NULL; mPollEnv = NULL; if (mExceptionObj) { env->Throw(mExceptionObj); env->DeleteLocalRef(mExceptionObj); mExceptionObj = NULL; } }1.
pollOnce()
system/core/libutils/include/utils/Looper.h
/** * 等待事件可用,以毫秒为单位可选超时。 * 为发生事件的所有文件描述符调用回调。 * 如果超时为零,则立即返回而不阻塞。 * 如果超时为负,则无限期等待直到事件出现。 * 如果之前使用wake() 唤醒轮询,则返回POLL_WAKE * 超时到期,没有调用回调,也没有其他文件 * 描述符已准备就绪。 * 如果调用了一个或多个回调,则返回 POLL_CALLBACK。 * 如果在给定之前没有数据等待超时,则返回 POLL_TIMEOUT。 * 如果等待过程中发生错误,则返回 POLL_ERROR。 * 如果文件描述符有数据,则返回一个 >= 0 包含标识符的值 * 并且它没有回调函数(这里需要调用者来处理它)。 * 在这个(并且只有这个)情况下,outFd、outEvents 和 outData 将包含投票 * 与 fd 关联的事件和数据,否则它们将被设置为 NULL。 * * 此方法在完成调用适当的回调之前不会返回 * 用于所有已发出信号的文件描述符。 */ int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData); inline int pollOnce(int timeoutMillis) { //timeOutMillis参数为超时等待时间。如果为-1,则表示无限等待,直到有事件发生为止。如果值为0,则无需等待立即返回。 return pollOnce(timeoutMillis, nullptr, nullptr, nullptr); }
pollOnce()
system/core/libutils/Looper.cpp
1.int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) { int result = 0; //一个无限循环 for (;;) { //mResponses是一个Vector,这里首先需要处理response while (mResponseIndex < mResponses.size()) { const Response& response = mResponses.itemAt(mResponseIndex++); int ident = response.request.ident; if (ident >= 0) { //首先处理那些没有callback的Response int fd = response.request.fd; int events = response.events; void* data = response.request.data; #if DEBUG_POLL_AND_WAKE ALOGD("%p ~ pollOnce - returning signalled identifier %d: " "fd=%d, events=0x%x, data=%p", this, ident, fd, events, data); #endif if (outFd != nullptr) *outFd = fd; if (outEvents != nullptr) *outEvents = events; if (outData != nullptr) *outData = data; //实际上,对于没有callback的Response,pollOnce只是返回它的ident, //并没有实际做什么处理。因为没有callback,所以系统也不知道如何处理 return ident; } } if (result != 0) { #if DEBUG_POLL_AND_WAKE ALOGD("%p ~ pollOnce - returning result %d", this, result); #endif if (outFd != nullptr) *outFd = 0; if (outEvents != nullptr) *outEvents = 0; if (outData != nullptr) *outData = nullptr; return result; } //调用pollInner函数。注意,它在for循环内部 result = pollInner(timeoutMillis); } }
Handler 的 sendMessageDelayed 或者 postDelayed 是如何实现的
上面说到,在向 MessageQueue 队列中插入 Message 时,会根据 Message 的执行时间排序。而消息的延时处理的核心实现是在获取 Message 的阶段,接下来看下 MessageQueue 的 next 方法。
MessageQueue.next()
Message next() { ... for (;;) { ... nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // 尝试检索下一条消息。 找到就返回。 //获取系统时间。 final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null) { if (now < msg.when) { // 下一条消息未准备好。 设置超时以在准备好时唤醒。 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // 收到一条消息。 //mBlocked:指示 next() 是否在 pollOnce() 中以非零超时阻塞等待。 mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); //返回 Message return msg; } } else { // 没有更多的消息。 nextPollTimeoutMillis = -1; } } } }
上面表示从 MessageQueue 中取出一个 Message,但是当前的系统时间小于 Message.when,因此会计算一个 timeout,目的是实现在 timeout 时间段后再将 UI 线程唤醒,因此后续处理 Message 的代码只会在 timeout 时间之后才会被 CPU 执行。
注意:在上述代码中也能看出,如果当前系统时间大于或等于 Message.when,那么会返回 Message 给 Looper.loop()。但是这个逻辑只能保证在 when 之前消息不被处理,不能够保证一定在 when 时被处理。
总结