我有一个.lrc文件,我需要使用CountDownTimer
浏览每一行。我尝试使用AsyncTask
来执行此操作,但出现错误:
Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
在行new CountDownTimer...
上,我尝试使用runnable来执行此操作,但仍然遇到相同的错误。我的目标是使它遍历如下所示的.lrc文件中的每一行:
[00:04.15]Help me, it's like the walls are caving in
[00:10.46]Sometimes I feel like giving up
[00:13.63]But I just can't
...
我不确定这样做的效率如何。我正在考虑遍历doInBackground()
中的每一行。如果有更好的方法,请告诉我。但是首先,为什么我会得到EXCEPTION
?
仅需注意..我已尽可能地简化了代码,因此可以更容易地理解我要执行的操作。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyView myView = new
myView.play();
}
}
public class MyView{
public void play() {
new CustomAsync().execute();
}
}
class CustomAsync extends AsyncTask<Lyric, Void, Void> {
protected Void doInBackground(Lyric... param) {
startLyricCountDownTimer(param);
return null;
}
protected void onPostExecute(Void param) {
//Print Toast or open dialog
}
private void startLyricCountDownTimer(Lyric lyric){
new CountDownTimer(30000, 10) { //This is where it throws the error
public void onTick(long millisUntilFinished) {
//Do the thing
}
public void onFinish() {
}
}.start();
}
}
编辑
最好使用AsyncTask
并按照建议的Son Truong
进行操作,还是对每个lrc行使用以下代码?
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
new CountDownTimer(millisInFuture,countDownInterval) {
@Override
public void onTick(
答案 0 :(得分:2)
CountDownTimer使用处理程序将消息发布到具有Looper的线程的消息队列中。根据创建CountDownTimer实例的位置,将在哪个线程上调用onTick
和onFinish
。
在您的情况下,因为您是在AsyncTask的doInBackground
方法中创建CountDownTimer实例,所以将在AsyncTask线程上调用这两个方法。
在CountDownTimer的构造函数中,还将创建Handler实例。处理程序将检查当前线程是否具有Looper,否则将抛出带有消息的RuntimeException。
无法在未调用的线程内创建处理程序 Looper.prepare()
因为AsyncTask使用的线程没有Looper,所以您的应用程序崩溃了。
我的建议是在doInBackground
方法中,打开与.lrc文件的连接并读取每一行,对于读取的每一行,请使用runOnUIThread
将该行发送到UI线程(然后,您可以处理通过在屏幕上显示Toast等读取的行)。
更新:我将演示如何从文件中逐行读取文件,然后每3秒将其显示在文本视图中。
首先编写一个类,逐行从输入流中读取
static class ReadLyricTask extends AsyncTask<InputStream, String, Void> {
WeakReference<MainActivity> mMainActivity;
ReadLyricTask(MainActivity activity) {
mMainActivity = new WeakReference<>(activity);
}
@Override
protected Void doInBackground(InputStream... inputStreams) {
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStreams[0]));
String line;
try {
while ((line = reader.readLine()) != null) {
publishProgress(line);
}
} catch (IOException e) {
// Do nothing.
} finally {
try {
inputStreams[0].close();
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onProgressUpdate(String... values) {
MainActivity activity = mMainActivity.get();
if (activity != null) {
activity.displayLyricLineOnTextView(values[0]);
}
}
}
然后在 MainActivity
中使用它public class MainActivity extends AppCompatActivity {
private static final int UPDATE_LYRIC_TEXT_INTERVAL = 3000; // Change lyric text each 3 seconds.
private int mCurrentInterval = 0;
private TextView mLyricTextView;
private Handler mHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLyricTextView = findViewById(R.id.lyricText);
// I put a file named lyric.lrc in raw folder, for your case just open an input stream from a file.
InputStream inputStream = getResources().openRawResource(R.raw.lyric);
new ReadLyricTask(this).execute(inputStream);
}
private void displayLyricLineOnTextView(final String lyricLine) {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mLyricTextView.setText(lyricLine);
}
}, mCurrentInterval);
mCurrentInterval += UPDATE_LYRIC_TEXT_INTERVAL;
}
}
答案 1 :(得分:-1)
CountDownTimer在单独的线程中运行,不需要asynctask来运行计时器。最佳解决方案是创建一个服务并使make服务触发计时器。 由于计时器在非ui线程上运行,因此在更新ui时,请确保从UI线程进行更新。您可以使用UI处理程序或runOnUithread方法来更新视图。