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 毫秒(或更多)会发生什么:
- 计时器调用
onTick()
。
onTick()
在 MediaPlayer.create()
. 内阻塞约 100 毫秒
mp
开始播放,onTick()
returns。 (到目前为止,一切顺利!)
- 计时器立即意识到另一个
onTick()
呼叫到期!最后一个在 100 多毫秒前开始!
onTick()
几乎立即再次被调用。很快,它到达 stopPlaying()
调用。但是你只是 开始 大约 1 毫秒前玩!
这会导致您的计时器将所有时间都花在 MediaPlayer.create()
上,而几乎没有时间实际播放声音。
请注意,使用编写的代码,它将尝试在倒计时的最后 3 秒内播放大约 30 次声音(因为理想情况下,滴答声相隔 100 毫秒)。如果您的目的是只播放声音 3 次,您可能需要将第二个 if
块移动到第一个块内。这样,您只会在 secondsLeft
实际发生变化时尝试播放。这实际上会改善原来的问题,您可能不需要任何进一步的更改。
但是如果你想进一步优化,请注意你可以提前准备 mp
——比如,当应用程序启动时——然后简单地重用它:而不是 release()
-ing每次都是 stop()
它,prepare()
它(并且 不要 reset()
它)。这将为下一场比赛做好准备。您甚至可以为 beepend
创建一个单独的 MediaPlayer
,并且您可以在应用程序初始化期间准备它们。
我正在尝试从 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 毫秒(或更多)会发生什么:
- 计时器调用
onTick()
。 onTick()
在MediaPlayer.create()
. 内阻塞约 100 毫秒
mp
开始播放,onTick()
returns。 (到目前为止,一切顺利!)- 计时器立即意识到另一个
onTick()
呼叫到期!最后一个在 100 多毫秒前开始! onTick()
几乎立即再次被调用。很快,它到达stopPlaying()
调用。但是你只是 开始 大约 1 毫秒前玩!
这会导致您的计时器将所有时间都花在 MediaPlayer.create()
上,而几乎没有时间实际播放声音。
请注意,使用编写的代码,它将尝试在倒计时的最后 3 秒内播放大约 30 次声音(因为理想情况下,滴答声相隔 100 毫秒)。如果您的目的是只播放声音 3 次,您可能需要将第二个 if
块移动到第一个块内。这样,您只会在 secondsLeft
实际发生变化时尝试播放。这实际上会改善原来的问题,您可能不需要任何进一步的更改。
但是如果你想进一步优化,请注意你可以提前准备 mp
——比如,当应用程序启动时——然后简单地重用它:而不是 release()
-ing每次都是 stop()
它,prepare()
它(并且 不要 reset()
它)。这将为下一场比赛做好准备。您甚至可以为 beepend
创建一个单独的 MediaPlayer
,并且您可以在应用程序初始化期间准备它们。