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

时间:2018-11-02 16:38:22

标签: 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(

2 个答案:

答案 0 :(得分:2)

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

在您的情况下,因为您是在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方法来更新视图。