在TimerTask.run中执行命令的顺序错误在用Java编写的Android应用中

问题描述

我正在编写一个简单的Android应用程序(使用Java),该应用程序应该能够加载* .txt文件,并每0.5秒一次显示一个单词。我设法将文本文件内容放入一个String数组,该数组在每个索引下存储一个单词。我正在使用Timer和TimerTask在给定间隔的TextView中显示数组中的单词。为了实现这个目标,我的TimerTask run()方法首先调用TextViev.setText()方法,该方法以当前索引显示数组中的项,然后递增索引(或者我认为)。事实证明,首先增加索引,然后调用TextViev.setText()方法(尽管TimerTimer的重写run()方法中的命令顺序)。我的问题是:为什么命令按此顺序执行,我该如何解决

TimerTask readingTimerTask = new TimerTask() {
            @Override
            public void run() {
                if (wordindex < textArr.length - 1) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            tvWords.setText(textArr[wordindex]);
                            Log.i(LOG_TAG,"setText() called. wordindex: " + String.valueOf(wordindex));
                        }
                    });
                    wordindex++;
                    Log.i(LOG_TAG,"wordindex incremented. New value of wordindex: " + String.valueOf(wordindex));
                } else {
                    stopReading();
                }
            }
        };
mTimer = new Timer();
Log.i(LOG_TAG,"Timer is starting. wordindex: " + String.valueOf(wordindex));
mTimer.schedule(readingTimerTask,100,displayTimeInMs);

Logcat看起来像这样:

D/MainActivity: wordindex in onActivityResult (file loaded): 0
I/MainActivity: Timer is starting. wordindex: 0
I/MainActivity: wordindex incremented. New value of wordindex: 1
I/MainActivity: setText() called. wordindex: 1
I/MainActivity: wordindex incremented. New value of wordindex: 2
I/MainActivity: setText() called. wordindex: 2
I/MainActivity: wordindex incremented. New value of wordindex: 3
I/MainActivity: setText() called. wordindex: 3
I/MainActivity: wordindex incremented. New value of wordindex: 4
I/MainActivity: setText() called. wordindex: 4
I/MainActivity: wordindex incremented. New value of wordindex: 5
I/MainActivity: setText() called. wordindex: 5
I/MainActivity: Timer has been stopped. wordindex: 5

解决方法

为什么按此顺序执行命令?

从后台线程调用时,runOnUiThread()不会等待run()中的命令完成才返回。它安排它们在单独线程:UI线程上执行。

我假设您的代码段是在UI(或“主”)线程上调用的。同时,您的TimerTask引入了第二个线程:内部线程。 IOW,您已经创建了race condition

在这种情况下,不能保证wordIndex++首先执行,但通常会“赢得”比赛,因为UI线程需要一些时间来处理Runnable

我该如何解决?

您可能没有打算在程序中引入多个线程,这可能会使事情变得非常复杂,而对于新手程序员则不适用。您可能要使用CountDownTimer而不是TimerTask。这将帮助您将所有内容保留在UI线程上。