Search code examples
javaandroidcountdowntimer

CountDownTimer freezes UI when trying to reproduce sound


I'm trying to reproduce the beep.wav sound each second from 3 to 1 , for example, reproduce 3 , 2, and 1 the beep sound, and then when finish reproduce the beependsound.

For some reason only the beependsound is playing but when reaching second 3 it seems the ui freezes for a sec and then the numbers decrease fast to 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();
    }

I spect this to work as described above, any hint ?


Solution

  • MediaPlayer.create() can be a fairly expensive call. Consider what happens if it takes approximately 100ms (or even more):

    1. The timer calls onTick().
    2. onTick() blocks for about 100ms inside MediaPlayer.create().
    3. mp starts playing, and onTick() returns. (So far, so good!)
    4. Immediately, the timer realizes another onTick() call is due! The last one started over 100ms ago!
    5. onTick() is called again, almost immediately. Very quickly, it reaches the stopPlaying() call. But you only started playing about 1ms ago!

    This leads to a situation where your timer spends all its time in MediaPlayer.create(), and almost no time actually playing the sound.

    Note that, with the code as written, it will attempt to play the sound approximately 30 times in the last 3 seconds of the countdown (since the ticks are ideally 100ms apart). If your intent was to play the sound only 3 times, you may want to move your second if block to inside the first. That way, you only attempt play when secondsLeft actually changes. This will actually ameliorate the original problem, and you may not need any further changes.

    But if you want to optimize further, note that you can prepare mp in advance -- say, when the app starts up -- and simply reuse it: Instead of release()-ing it each time, just stop() it, and prepare() it (and don't reset() it). That'll leave it ready for the next play. You could even create a separate MediaPlayer just for beepend, and you could prepare them both during app initialization.