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

Won*_*der 3 java android handler runnable postdelayed

我试图通过浏览 Android Studio 中提供的全屏活动模板来了解其功能,特别是我们removeCallbacks()结合使用的一个特殊原因。postDelayed()在全屏活动模板中,当触摸屏幕时,它将在一定毫秒数后显示/隐藏状态栏和导航/系统栏,在此模板的情况下为 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);
}
Run Code Online (Sandbox Code Playgroud)

我知道它removeCallbacks()用于从消息队列中删除待处理的消息/可运行对象,但是除了满足条件并且我们不再希望执行待处理的消息/可运行对象之外,我们是否有特殊原因使用它?

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

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

以下是 .gif 形式的差异。使用removeCallbacks()是预期的行为,而注释掉removeCallbacks()会导致不需要的“滥用”行为:

使用removeCallbacks()

没有removeCallbacks()

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

azi*_*ian 7

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

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

SHOW - HIDE - SHOW - HIDE - ...
Run Code Online (Sandbox Code Playgroud)

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

另一方面,当使用时,removeCallbacks()您表明您不再对相反的事件感兴趣并且不希望该事件完全执行。例如,如果我们遇到显示系统栏的情况,那么下一次单击将HIDE在 300 毫秒后启动一个事件,并且您明确告知,“嘿,如果发布了一些应该显示SHOW系统栏的事件,那么取消它们”

handler.removeCallbacks(showRunnable);
handler.postDelayed(hide, 300);
Run Code Online (Sandbox Code Playgroud)

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

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

EMPTY
Run Code Online (Sandbox Code Playgroud)

点击发生:

HIDE
Run Code Online (Sandbox Code Playgroud)

点击发生:

SHOW (HIDE is being removed from queue)
Run Code Online (Sandbox Code Playgroud)

点击发生:

HIDE (SHOW is being removed from queue)
Run Code Online (Sandbox Code Playgroud)

因此,最后,当 300ms 过去并且该消息没有从队列中删除时,只会执行最后一个事件。