返回

从BUG说起:View.post/remove runnable失败的原因分析

Android

前言

在Android开发中,View.post()View.removeCallbacks()这两个方法经常被用来在UI线程中执行任务。然而,有时我们会遇到这些方法执行失败的情况。本文将从源码分析的角度探讨造成这种失败的可能原因。

View.post()的工作原理

View.post()方法将一个Runnable对象添加到视图的消息队列中。当视图在下一个UI更新周期时,它会依次处理消息队列中的任务。

View.removeCallbacks()的工作原理

View.removeCallbacks()方法从视图的消息队列中移除指定的任务。这个方法可以确保任务不会在UI更新周期中执行。

可能的失败原因

1. 视图未附加到窗口

View.post()View.removeCallbacks()方法都要求视图附加到窗口。如果不满足这个条件,这些方法将失败。

if (mView == null || !mView.isAttachedToWindow()) {
    return false;
}

2. 任务已经被执行

View.removeCallbacks()方法只能移除尚未执行的任务。如果任务已经执行,该方法将返回false

Message msg = obtainMessage(MSG_RUN_CALLBACKS);
msg.obj = runnable;
mHandler.sendMessageAtFrontOfQueue(msg);

3. 消息队列已满

每个视图都有一个消息队列,用于处理任务。如果消息队列已满,View.post()方法将失败。

if (!queue.offer(msg)) {
    Log.w(TAG, "Post failed. size()=" + queue.size());
    return false;
}

4. handleMessage()方法未被覆盖

View类中定义了handleMessage()方法,用于处理消息队列中的任务。如果这个方法没有被子类覆盖,View.post()方法将失败。

@Override
public void handleMessage(Message msg) {
    switch (msg.what) {
        case MSG_RUN_CALLBACKS:
            runCallbacks((ArrayList<View.AttachInfo.PendingRunnables>) msg.obj);
            break;
    }
}

避免失败的建议

为了避免View.post()View.removeCallbacks()方法失败,建议遵循以下建议:

  • 确保视图已附加到窗口。
  • 在任务执行之前使用View.removeCallbacks()方法将其移除。
  • 监控消息队列的大小,并采取措施防止队列溢出。
  • 覆盖View类的handleMessage()方法,以正确处理任务。

结束语

通过分析View.post()View.removeCallbacks()方法的源码,我们可以了解导致这些方法失败的可能原因。遵循上述建议,可以避免这些失败,并确保任务在UI线程中正确执行。