Замена CountDownTimer на Handler и Runnable для повышения производительности

У меня есть игра, основанная на CountDownTimer, который постоянно повторяет обратный отсчет. Этот обратный отсчет подсчитывает время для реакции пользователя на какое-либо действие, связанное с number, если пользователь реагирует, onFinish() вызывается каким-либо clickListener или сам по себе, если время истекло. В зависимости от succesCondition() вызывается метод success или fail, и эти методы определяют, запущена ли игра.

OnCreate

loop = gameLoop(time).start();

Основная деятельность

public CountDownTimer gameLoop(int time){
    return new CountDownTimer(time, time+100) {
        @Override
        public void onTick(long millisUntilFinished) {

        }

        @Override
        public void onFinish() {
            if (!Conditions.succesCondition(number)) {
                success();
            } else {
                fail();
            }
        }
    };
}

public void success() {
    loop.cancel();
    scoreCount++;
    animation.start();
}

public void fail(){
   loop.cancel();
}

Однако этот таймер работает в основном потоке и обеспечивает хорошо известную проблему skipped xx frames, your app might be doing too much work on its main thread, и я обнаружил, что это обычная проблема для CountDownTimer, и замена ее на Handler является решением.

Я не могу поместить этот таймер в AsyncTask, потому что он выполняет в основном задачи, связанные с пользовательским интерфейсом (TextViews, TextSwitcher, некоторые progressBar и т. д. В методе success(). Я не вставлял это в код этих методов для более четкого представления основной проблемы. I Я пытаюсь реконструировать концепцию, подобную CountDownTimer, с обработчиком и запускаемой программой для замены моего таймера, но на самом деле я ни с чем не застрял. Как видите, я использую только метод onFinish, onTick не нужен.

не так-то просто выполнить обработчик с поведением, аналогичным countDownTimer

Patroy 25.03.2018 16:19

Нет, не знаю, я раньше видел класс countDownTimer, но не мог понять, как его переписать сам

Patroy 26.03.2018 09:55

так что непонятного в Handler у вас есть? вызовы каких методов трудно понять?

pskink 26.03.2018 10:08

Не то чтобы что-то непонятно, но если я сделаю это просто переписав класс таймера, то разницы в производительности не будет.

Patroy 26.03.2018 10:24

поэтому CountDownTimer не является виновником, но ваши методы onTick слишком много работают с потоком пользовательского интерфейса

pskink 26.03.2018 10:27

onTick пуст, там ничего нет

Patroy 26.03.2018 10:34

как часто вы звоните в gameLoop? Кроме того, ваш CountDownTimer(time, time+100) бесполезен - проверьте документацию по обоим параметрам - чего на самом деле вы хотите достичь?

pskink 26.03.2018 10:47

Я знаю, что это бессмысленно, но мне было что сюда добавить, я вообще-то не очень хорошо понял второй параметр ... Для большей наглядности это моя игра: play.google.com/store/apps/…. gameLoop.start вызывается каждый раз, когда вы набираете очки

Patroy 26.03.2018 10:52
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
8
942
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы можете справиться с этой ситуацией, используя AsyncTask, а также переопределив метод onProgressUpdate.

Вот пример о том, как вы можете добиться поведения для взаимодействия с вашим основным потоком из AsyncTask. В примере показано обновление загрузки, которое можно легко преобразовать в вашу конкретную проблему с таймером.

Обновлять

In my case almost all code would be in onProgressUpdate, would it still make any sense?

Нет, вашего кода не будет в onProgressUpdate. Метод onProgressUpdate будет только обновлять ваш таймер в вашем пользовательском интерфейсе. Насколько я понял, успех и неудача также будут зависеть от действий пользователя. Затем эти действия запускаются, вы можете остановить AsyncTask, чтобы обновить свой таймер. Вам просто нужно AsyncTask, чтобы время от времени обновлять значение таймера.

Когда Activity закончит, на ваш AsyncTask поступит обратный вызов. См. Функцию mNotificationHelper.completed(); в приведенном выше примере. Когда вы получаете уведомление на вашем Activity о завершении таймера, вы можете выполнить следующую задачу там.

public void completed()    {
    if (!Conditions.succesCondition(number)) {
        success();
    } else {
        fail();
    }
}

В моем случае почти весь код будет в onProgressUpdate, будет ли это иметь смысл?

Patroy 25.03.2018 13:22

Не только таймер связан с потоком пользовательского интерфейса, у меня есть анимации, textViews и т. д., Которые обновляются при каждом обратном отсчете, который повторяется каждые 3 секунды.

Patroy 26.03.2018 09:58

Да, в onProgressUpdate необходимо обновить те, которые связаны с обратным отсчетом.

Reaz Murshed 26.03.2018 10:17

Я предлагаю использовать комбинацию java.util.Timer, java.util.TimerTask и Activity.runOnUiThread(). Сначала создайте Timer и вызовите один из его методов schedule...(). Любое действие, которое необходимо выполнить в основном потоке (ui), можно обернуть в runOnUiThread(() -> { ...}). Обязательно вызовите cancel() на TimerTask и Timer, если эти объекты больше не нужны. Отмена Timer также отменяет TimerTask.

Вот как это может выглядеть:

public class TimerTaskActivity extends Activity {

    Timer timer;
    TimerTask timerTask;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.timertask);
        ...
    }

    @Override
    protected void onStart() {
        super.onStart();
        timer = new Timer();
        timerTask = new TimerTask() {

            @Override
            public void run() {
                runOnUiThread(() -> {
                    ....
                });
            }
        };
        timer.scheduleAtFixedRate(timerTask, 2000, 2000);
    }

    @Override
    protected void onPause() {
        super.onPause();
        timer.cancel();
    }
}
Ответ принят как подходящий

ОК. Я наконец понял, как справиться с этим с помощью обработчика (хе-хе):

public void startGameAction() {

    //My game actions

    handler = new Handler();
    runnable = () -> {
        if (!Conditions.succesCondition(number)) {
            success();
        } else {
            fail();
        }
    };
    handler.postDelayed(runnable,time);
}

public void success(){
        handler.removeCallbacks(runnable);
        handler = null;

        scoreCount++;
        //other stuff
        startGameAction();
    }

 private void fail() {
    handler.removeCallbacks(runnable);
    //other stuff
}

onCreate только вызов startGame, обработчик и запускаемый объект, определенные как поля класса

startGameAction();

Другие вопросы по теме