用 Handler 和 Runnable 替换 CountDownTimer 以获得更好的性能

Replacing CountDownTimer with Handler and Runnable for better performance

我有一个基于 CountDownTimer 的游戏,它会不断重复倒计时。此倒计时正在计算用户对与 number 相关的某些操作做出反应的时间,如果用户反应 onFinish() 则被某些 clickListener 调用,或者如果时间到了则由其自身调用。根据 succesCondition(),方法 successfail 被调用,这些方法定义游戏是否仍然 运行。

创建时

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.Timerjava.util.TimerTaskActivity.runOnUiThread() 的组合。首先创建一个 Timer 并调用其中一个 schedule...() 方法。任何需要在主 (ui) 线程上完成的操作都可以包装在 runOnUiThread(() -> { ...}) 中。如果不再需要这些对象,请务必在 TimerTaskTimer 上调用 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();