用 Handler 和 Runnable 替换 CountDownTimer 以获得更好的性能
Replacing CountDownTimer with Handler and Runnable for better performance
我有一个基于 CountDownTimer 的游戏,它会不断重复倒计时。此倒计时正在计算用户对与 number
相关的某些操作做出反应的时间,如果用户反应 onFinish()
则被某些 clickListener
调用,或者如果时间到了则由其自身调用。根据 succesCondition()
,方法 success
或 fail
被调用,这些方法定义游戏是否仍然 运行。
创建时
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
中,因为它主要执行 UI 相关任务(TextViews、TextSwitcher、success()
方法中的一些 progressBar 等。我没有将其放入这些方法的代码中,以便更清楚地了解主要问题。我正在尝试重建 CountDownTimer
- 类似于带有处理程序和可运行的概念以替换我的计时器,但实际上我什么都没有。正如你可以看到我只使用了 onFinish
方法,onTick
不是必需的。
您可以使用 AsyncTask
以及覆盖 onProgressUpdate
方法来处理这种情况。
这里 an example 介绍了如何实现与来自 AsyncTask
的主线程交互的行为。该示例显示了下载的更新,可以轻松地将其转换为您的特定计时器问题。
更新
In my case almost all code would be in onProgressUpdate, would it
still make any sense?
不,您的代码不会在 onProgressUpdate
中。 onProgressUpdate
方法只会更新 UI 中的计时器。据我所知,成功和失败也将根据用户操作触发。然后触发这些操作,您也可以停止 AsyncTask
来更新您的计时器。您只需要 AsyncTask
不时更新定时器值。
当 AsyncTask
完成时,您将在 Activity
中收到回调。请参见上例中的 mNotificationHelper.completed();
函数。当计时器结束时,您会在 Activity
中收到通知,然后您可能会在那里执行以下任务。
public void completed() {
if (!Conditions.succesCondition(number)) {
success();
} else {
fail();
}
}
我建议使用 java.util.Timer
、java.util.TimerTask
和 Activity.runOnUiThread()
的组合。首先创建一个 Timer
并调用其中一个 schedule...()
方法。任何需要在主 (ui) 线程上完成的操作都可以包装在 runOnUiThread(() -> { ...})
中。如果不再需要这些对象,请务必在 TimerTask
和 Timer
上调用 cancel()
。取消 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 仅定义为 class 字段的 startGame 调用、处理程序和可运行对象
startGameAction();
我有一个基于 CountDownTimer 的游戏,它会不断重复倒计时。此倒计时正在计算用户对与 number
相关的某些操作做出反应的时间,如果用户反应 onFinish()
则被某些 clickListener
调用,或者如果时间到了则由其自身调用。根据 succesCondition()
,方法 success
或 fail
被调用,这些方法定义游戏是否仍然 运行。
创建时
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
中,因为它主要执行 UI 相关任务(TextViews、TextSwitcher、success()
方法中的一些 progressBar 等。我没有将其放入这些方法的代码中,以便更清楚地了解主要问题。我正在尝试重建 CountDownTimer
- 类似于带有处理程序和可运行的概念以替换我的计时器,但实际上我什么都没有。正如你可以看到我只使用了 onFinish
方法,onTick
不是必需的。
您可以使用 AsyncTask
以及覆盖 onProgressUpdate
方法来处理这种情况。
这里 an example 介绍了如何实现与来自 AsyncTask
的主线程交互的行为。该示例显示了下载的更新,可以轻松地将其转换为您的特定计时器问题。
更新
In my case almost all code would be in onProgressUpdate, would it still make any sense?
不,您的代码不会在 onProgressUpdate
中。 onProgressUpdate
方法只会更新 UI 中的计时器。据我所知,成功和失败也将根据用户操作触发。然后触发这些操作,您也可以停止 AsyncTask
来更新您的计时器。您只需要 AsyncTask
不时更新定时器值。
当 AsyncTask
完成时,您将在 Activity
中收到回调。请参见上例中的 mNotificationHelper.completed();
函数。当计时器结束时,您会在 Activity
中收到通知,然后您可能会在那里执行以下任务。
public void completed() {
if (!Conditions.succesCondition(number)) {
success();
} else {
fail();
}
}
我建议使用 java.util.Timer
、java.util.TimerTask
和 Activity.runOnUiThread()
的组合。首先创建一个 Timer
并调用其中一个 schedule...()
方法。任何需要在主 (ui) 线程上完成的操作都可以包装在 runOnUiThread(() -> { ...})
中。如果不再需要这些对象,请务必在 TimerTask
和 Timer
上调用 cancel()
。取消 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 仅定义为 class 字段的 startGame 调用、处理程序和可运行对象
startGameAction();