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

Running CountDownTimer inside AsyncTask throws java.lang.RuntimeException - Looper.prepare()

我有一个 .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 运行s 在单独的线程中,不需要异步任务到 运行 timer.Best 解决方案是创建服务并使服务触发计时器。 作为非 ui 线程上的计时器 运行,在更新 ui 时确保从 UI thread.You 更新可以使用 UI 处理程序或 运行OnUithread 方法更新视图。

CountDownTimer 使用处理程序将消息 post 发送到具有 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 秒在文本视图中显示一次。

先写一个class,逐行从输入流中读取

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