为什么要将 removeCallbacks() 与 postDelayed() 一起使用?

Why to use removeCallbacks() with postDelayed()?

我试图通过浏览中提供的全屏 Activity 模板来了解功能,尤其是我们将 removeCallbacks()postDelayed() 结合使用的一个特殊原因Android工作室。在全屏 activity 模板中,当屏幕被触摸时,它会在一定的毫秒数后 show/hide 状态栏和 navigation/system 栏,在这个模板的例子中是 3000 毫秒。


private void hide() {
    // Hide UI first
    ActionBar actionBar = getSupportActionBar();
    if (actionBar != null) {
        actionBar.hide();
    }
    mControlsView.setVisibility(View.GONE);
    mVisible = false;

    // Schedule a runnable to remove the status and navigation bar after a delay
    mHideHandler.removeCallbacks(mShowPart2Runnable); // <------ Comment/uncomment
    mHideHandler.postDelayed(mHidePart2Runnable, UI_ANIMATION_DELAY);
}

@SuppressLint("InlinedApi")
private void show() {
    // Show the system bar
    mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
    mVisible = true;

    // Schedule a runnable to display UI elements after a delay
    mHideHandler.removeCallbacks(mHidePart2Runnable); // <------ Comment/uncomment
    mHideHandler.postDelayed(mShowPart2Runnable, UI_ANIMATION_DELAY);
}

/**
 * Schedules a call to hide() in [delay] milliseconds, canceling any
 * previously scheduled calls.
 */
private void delayedHide(int delayMillis) {
    mHideHandler.removeCallbacks(mHideRunnable); // <------ Comment/uncomment
    mHideHandler.postDelayed(mHideRunnable, delayMillis);
}

我知道 removeCallbacks() 用于从消息队列中删除 pending messages/runnables 但是有没有特殊原因我们会使用它比如果满足条件并且我们不再希望执行挂起的 messages/runnables?

我问这个是因为关于全屏 Activity 模板,我在这里有点困惑,因为似乎如果我在使用 postDelayed() 时不调用 mHideHandler.removeCallbacks(Runnable) 那么它将允许用户向 hide/show 方法发送垃圾邮件。对于垃圾邮件,看起来过渡动画被打断了,动画甚至可能在垃圾邮件期间卡住,因为它在过渡期间的某个点停止了。但是,如果我确实在 postDelayed() 之前调用 mHideHandler.removeCallbacks(Runnable),那么它将防止用户向 hide/show 方法发送垃圾邮件,这很好。

简而言之,我想我的问题是 mHideHandler.removeCallbacks(Runnable) 如何防止在与 postDelayed() 一起使用时调用方法的垃圾邮件?问题与此类似question,但我希望得到关于为什么会发生这种情况的解释。

以下是 .gif 格式的区别。使用 removeCallbacks() 是预期的行为,而注释掉 removeCallbacks() 会导致不需要的 "abuse" 行为:

With removeCallbacks()

Without removeCallbacks()

编辑:添加到 .gifs 中并向代码中的 removeCallbacks() 添加注释,以帮助识别我正在谈论的代码的哪一部分。

每次执行单击时,您都会向 MessageQueue 发送一个事件,该事件将在 UI_ANIMATION_DELAY 毫秒(假设为 300 毫秒)后执行。

现在,当您执行连续点击时,您将以这种方式发布事件:

SHOW - HIDE - SHOW - HIDE - ...

如果您不执行 removeCallbacks(),所有这些消息都将被执行,这意味着将执行每个 SHOWHIDE 操作,从而导致这样的故障行为。

另一方面,当使用 removeCallbacks() 时,您表示您不再对相反的事件感兴趣并且不希望完全执行该事件。例如,如果我们遇到显示系统栏的情况,那么下一次点击将启动一个 HIDE 事件,在 300 毫秒后发生,并且你明确地告诉,"hey, if there are some events posted that should SHOW the system bar, then cancel them"

handler.removeCallbacks(showRunnable);
handler.postDelayed(hide, 300);

这给您带来的是,每次您执行连续的点击事件时,已经发布在队列中的相反事件消息将被取消。这确保每次只有 1 条消息发布到队列中。

系统UI可见,当前消息队列:

EMPTY

点击次数:

HIDE

点击次数:

SHOW (HIDE is being removed from queue)

点击次数:

HIDE (SHOW is being removed from queue)

因此,最后,当经过 300 毫秒且此消息未从队列中删除时,只会执行最后一个事件。