У меня есть игра, основанная на 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, но не мог понять, как его переписать сам
так что непонятного в Handler у вас есть? вызовы каких методов трудно понять?
Не то чтобы что-то непонятно, но если я сделаю это просто переписав класс таймера, то разницы в производительности не будет.
поэтому CountDownTimer не является виновником, но ваши методы onTick слишком много работают с потоком пользовательского интерфейса
onTick пуст, там ничего нет
как часто вы звоните в gameLoop? Кроме того, ваш CountDownTimer(time, time+100) бесполезен - проверьте документацию по обоим параметрам - чего на самом деле вы хотите достичь?
Я знаю, что это бессмысленно, но мне было что сюда добавить, я вообще-то не очень хорошо понял второй параметр ... Для большей наглядности это моя игра: play.google.com/store/apps/…. gameLoop.start вызывается каждый раз, когда вы набираете очки




Вы можете справиться с этой ситуацией, используя 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, будет ли это иметь смысл?
Не только таймер связан с потоком пользовательского интерфейса, у меня есть анимации, textViews и т. д., Которые обновляются при каждом обратном отсчете, который повторяется каждые 3 секунды.
Да, в onProgressUpdate необходимо обновить те, которые связаны с обратным отсчетом.
Я предлагаю использовать комбинацию 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();
не так-то просто выполнить обработчик с поведением, аналогичным countDownTimer