执行顺序与 Thread.sleep 的使用混淆

order of execution mixed up with use of Thread.sleep

我正在为我的 android 应用程序编写一个方法,我在其中使 RecyclerView 不可见,而 ProgressBar 可见。然后我执行一些逻辑,然后将两个视图重置为其原始可见性状态。

只需 setVisibility() 调用,它就会按预期工作。但是,我也需要在执行完逻辑后直接调用Thread.sleep()强制等待

最初,我在尝试调用 setVisibility() 时遇到了问题。它什么也没做。我发现很多类似问题的问题,但不够相似;我找不到针对我的问题的解决方案。

创建一个简单调用 setVisibility() 的新方法,我发现它按预期工作。我开始逐行移动我的逻辑,直到它停止工作。

就目前而言,它仍然技术上正确设置可见性。然而,尽管从 setVisibility() 调用向下几行,我的 Thread.sleep() 似乎在 setVisibility() 之前强迫自己到 运行。我相信这是我最初的问题;从逻辑上讲,Thread.sleep() 之后的命令将直接在 运行 之后,并有效地在下一帧撤消我的 setVisibility()

这是我的方法:

public void SetMainInvisible(){
    mRecyclerView.setVisibility(View.INVISIBLE);
    mMainProgressBar.setVisibility(View.VISIBLE);

    mTrainAdapter.RefreshAll();

    Log.d("TEST", "FINISHED VIS");
    try {
        Thread.sleep(sSleepTime);
    } catch (InterruptedException exception) {
        // In the nature of a simple "Thread.sleep", there is no real reason to respond
        // directly to interruption. If the sleep request is interrupted, the best course
        // of action to preserve user experience is to simply move on. That said, we will
        // still "re-enable" the flag that tells us the thread was interrupted, in case we
        // should need to clarify if there was an interruption, later on. As is, this flag
        // will be reset to false as soon as the exception is thrown.
        Thread.currentThread().interrupt();
    }
}

根据我的直接观察,当它调用时,我的日志会打印 "FINISHED VIS"。然后我的应用程序进入 Thread.sleep() 阶段,并等待 3 秒。然后我的视图改变了它们的可见性,按照第一行 的指示。我的代码中没有其他地方 setVisibility()

我已经尝试进一步阅读 Thread.sleep,但所有参考资料都准确地表明了我所学的内容;当它执行时,它会强制进程 "sleep" 一段时间。它不应该强制该方法推迟所有其他逻辑直到它returns。相反,the examples at Tutorial Point 提供建议正常操作的逻辑和输出。

我知道我永远不应该打电话给 Thread.sleep(),但这是我为大学完成的练习的直接要求。为什么 Thread.sleep() 在任何其他命令之前强制自己 运行,尽管它处于方法的 end

更改可见性(或任何其他 layout/drawing 操作)不会对您的用户界面产生任何即时、同步的影响。相反,实际上只是 post 在 UI 线程的消息队列中编辑了一条消息,以便稍后处理更改。

禁止在 UI 线程上调用 sleep()。您正在阻塞 UI 线程,并且执行不会 return 处理队列中等待的 relayout/redraw 消息的消息处理程序。只有在 sleep() 之后才会对消息处理程序执行 return。

如果您需要在代码中添加延迟,请使用例如Handler#postDelayed()到post一个自己的Runnable到UI线程的消息队列,延迟后执行。

根据@laalto 的回答,我决定在研究 Handler#postDelayed() 之前以 AsyncTask 的形式测试我的方法(这是我们没有涉及的内容,我完全不熟悉用它)。我很高兴地报告它完全按预期工作。

对于那些更熟悉实施 AsyncTask 的人来说,这可能是一个合适的选择。

首先,我实现了一个内部异步class如下:

private class RefreshTimesAsyncTask extends AsyncTask<Void, Void, Void> {
    private long mSleepTime;
    public RefreshTimesAsyncTask (long sleepTime) {
        mSleepTime = sleepTime;
    }
    @Override
    protected void onPreExecute() {
        mMainProgressBar.setVisibility(View.VISIBLE);
        mRecyclerView.setVisibility(View.GONE);
        mTrainAdapter.RefreshAll();
    }

    @Override
    protected Void doInBackground(Void... params) {
        try {
            Thread.sleep(mSleepTime);
        } catch (InterruptedException exception) {
            // ...
            Thread.currentThread().interrupt();
        }
        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        mMainProgressBar.setVisibility(View.GONE);
        mRecyclerView.setVisibility(View.VISIBLE);
    }
}

然后我简单地调用 new RefreshTimesAsyncTask(sSleepTime).execute();,而不是我之前的 SetMainInvisible() 函数调用。由于需要将此值设置为 static[=27= 的性质,我还在我的 main class 的变量声明中设置了 static long sSleepTime=3000 ]not 无法在内部 class.

中声明 static 变量