CountDownTimer 在尝试重现声音时冻结 UI

CountDownTimer freezes UI when trying to reproduce sound

我正在尝试从 3 到 1 每秒重现 beep.wav 声音,例如,重现 3 、 2 和 1 的哔声,然后在完成时重现 beependsound。

出于某种原因,只有哔哔声在播放,但是当到达秒 3 时,ui 似乎冻结了一秒钟,然后数字快速减少到 0

private void stopPlaying(){
        if(mp!=null){
            try {
                mp.reset();
                mp.prepareAsync();
                mp.stop();
                mp.release();
                mp=null;
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }

    private void startCountDown() {

        aCounter = new CountDownTimer(10000, 100) {
            public void onTick(long millisUntilFinished) {


                if (Math.round((float) millisUntilFinished / 1000.0f) != secondsLeft) {
                    countDownTxt.setTextColor(getResources().getColor(R.color.white));
                    secondsLeft = Math.round((float) millisUntilFinished / 1000.0f);
                    countDownTxt.setText(String.valueOf(secondsLeft));
                }

                if (secondsLeft <= 3) {
                    countDownTxt.setTextColor(getResources().getColor(R.color.colorAccent));
                    stopPlaying();
                    mp = MediaPlayer.create(MainActivity.this, R.raw.beep);
                    mp.start();
                }
            }

            public void onFinish() {
                secondsLeft = 0;
                stopPlaying();
                mp = MediaPlayer.create(MainActivity.this, R.raw.beepend);
                mp.start();
                final Handler handler = new Handler();
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        aCounter.cancel();
                        startCountDown();
                    }
                }, 1000);

            }
        };
        aCounter.start();
    }

我认为它可以按上述方式工作,有任何提示吗?

MediaPlayer.create() 可能是一个相当昂贵的调用。考虑一下如果花费大约 100 毫秒(或更多)会发生什么:

  1. 计时器调用onTick()
  2. onTick()MediaPlayer.create().
  3. 内阻塞约 100 毫秒
  4. mp开始播放,onTick()returns。 (到目前为止,一切顺利!)
  5. 计时器立即意识到另一个 onTick() 呼叫到期!最后一个在 100 多毫秒前开始!
  6. onTick() 几乎立即再次被调用。很快,它到达 stopPlaying() 调用。但是你只是 开始 大约 1 毫秒前玩!

这会导致您的计时器将所有时间都花在 MediaPlayer.create() 上,而几乎没有时间实际播放声音。

请注意,使用编写的代码,它将尝试在倒计时的最后 3 秒内播放大约 30 次声音(因为理想情况下,滴答声相隔 100 毫秒)。如果您的目的是只播放声音 3 次,您可能需要将第二个 if 块移动到第一个块内。这样,您只会在 secondsLeft 实际发生变化时尝试播放。这实际上会改善原来的问题,您可能不需要任何进一步的更改。

但是如果你想进一步优化,请注意你可以提前准备 mp——比如,当应用程序启动时——然后简单地重用它:而不是 release()-ing每次都是 stop() 它,prepare() 它(并且 不要 reset() 它)。这将为下一场比赛做好准备。您甚至可以为 beepend 创建一个单独的 MediaPlayer,并且您可以在应用程序初始化期间准备它们。