问题描述
我正在编写一个简单的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线程上。