Запуск CountDownTimer внутри AsyncTask генерирует исключение java.lang.RuntimeException - Looper.prepare () - PullRequest
0 голосов
/ 02 ноября 2018

У меня есть файл .lrc, и мне нужно просмотреть каждую строку с CountDownTimer. Я пытался использовать AsyncTask, но я получаю ошибку:

Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

On line 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();
    }
}

EDIT Лучше пойти с 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 голосов
/ 03 ноября 2018

CountDownTimer использует обработчик для отправки сообщений в очередь сообщений потока, который имеет Looper. onTick и onFinish будут вызываться в каком потоке в зависимости от того, где вы создали экземпляр CountDownTimer.

В вашем случае, потому что вы создаете экземпляр CountDownTimer в doInBackground методе AsyncTask, поэтому эти два метода будут вызываться в потоке AsyncTask.

В конструкторе CountDownTimer он также создаст экземпляр обработчика. Обработчик проверит, имеет ли текущий поток Looper, если нет, он выдаст исключение RuntimeException с сообщением.

Невозможно создать обработчик внутри потока, который не вызвал Looper.prepare ()

Поскольку AsyncTask использует поток, в котором нет Looper, именно поэтому ваше приложение вылетает.

Мое предложение заключается в методе doInBackground, когда вы открываете соединение с файлом .lrc и читаете каждую строку, для каждой прочитанной строки используйте runOnUIThread, чтобы отправить строку в поток пользовательского интерфейса (тогда вы можете обработать строку, прочитанную там, отображение тоста на экране и т. д.).

Обновление: Я продемонстрирую, как читать строку за строкой из файла, а затем отображать его в текстовом представлении каждые 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;
    }
}
0 голосов
/ 02 ноября 2018

CountDownTimer работает в отдельном потоке, и для запуска таймера не требуется асинхронная задача. Лучшим решением будет создание службы и создание службы для запуска таймера. Поскольку таймер запускается в потоке без пользовательского интерфейса, при обновлении пользовательского интерфейса убедитесь, что вы обновляете из потока пользовательского интерфейса. Для обновления представления можно использовать обработчик пользовательского интерфейса или метод runOnUithread.

...