java - 在 AsyncTask 中运行 CountDownTimer 会抛出 java.lang.RuntimeException - Looper.prepare()

标签 java android android-asynctask countdowntimer looper

我有一个 .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(

最佳答案

CountDownTimer 使用 Handler 将消息发布到具有 Looper 的线程的消息队列。 onTickonFinish 将根据您创建 CountDownTimer 实例的位置在哪个线程上调用。

在您的情况下,因为您在 AsyncTask 的 doInBackground 方法中创建了 CountDownTimer 实例,所以这两个方法将在 AsyncTask 线程上调用。

在 CountDownTimer 的构造函数中,它也会创建 Handler 实例。 Handler 会检查当前线程是否有 Looper,如果没有,它会抛出一个 RuntimeException 和消息。

Can't create handler inside thread that has not called Looper.prepare()

因为 AsyncTask 使用没有 Looper 的线程,这就是您的应用程序崩溃的原因。

我的建议是在 doInBackground 方法中打开一个到 .lrc 文件的连接并读取每一行,对于读取的每一行,使用 runOnUIThread 将行发送到 UI 线程(然后您可以通过在屏幕上显示 Toast 等来处理那里读取的行)。

更新:我将演示如何从文件中逐行读取,然后每 3 秒将其显示在 TextView 中。

首先编写一个逐行读取输入流的类

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;
    }
}

关于java - 在 AsyncTask 中运行 CountDownTimer 会抛出 java.lang.RuntimeException - Looper.prepare(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53122598/

相关文章:

android - 在android中使用andengine动态加载级别

android - 用于测试 Android 客户端应用程序的自定义 Web 服务

android - 应用程序偶尔会在异步任务中崩溃

android - 如何创建异步任务以在加载屏幕上将声音加载到管理器中

JAVA TCP客户端不打印并断开连接

java - Retrofit 2 使用太多线程

java - 关于java(Quartz)调度的初学者问题

java - 在 GUI 中运行程序很慢

android - 滑动后如何恢复回收者 View 项目

android - fragment 中的 AsyncTask 不更新 UI