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 ?
MediaPlayer.create() can be a fairly expensive call. Consider what happens if it takes approximately 100ms (or even more):
The timer calls onTick().
onTick() blocks for about 100ms inside MediaPlayer.create().
mp starts playing, and onTick() returns. (So far, so good!)
Immediately, the timer realizes another onTick() call is due! The last one started over 100ms ago!
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.
Related
My project is a radio app and I want to add a sleep timer so that the user can set it to close the app after a specified time.
Please use startTimer(long ms) to start countdown timer and cancel timer to stop. And use wakelock to continue timer after screen off.
CountDownTimer cTimer = null;
void startTimer(long time) {
if(!wakeLock.isHeld()) {
PowerManager mgr = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);
wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SonaHeartLock");
wakeLock.acquire();
}
cTimer = new CountDownTimer(time, 1000) {
public void onTick(long millisUntilFinished) {
timerSync = millisUntilFinished;
}
public void onFinish() {
timerSync = 0;
System.exit(0);
}
};
cTimer.start();
}
//cancel timer
void cancelTimer() {
if (cTimer != null) {
timerSync = 0;
cTimer.cancel();
cTimer=null;
if(wakeLock!=null && wakeLock.isHeld())
wakeLock.release();
}
}
Thread.sleep(2000);
will pause for 2 seconds (i.e 2000 ms). In an Android app I wrote several years ago I actually had:
Thread.currentThread().sleep(2000);
but I'm pretty sure the .currentThread() was not needed. I was just learning Java at the time.
See Oracle's doc.
In more detail, what I actually had was a separate task:
class TimerUpdater extends AsyncTask<String, String, Void>{
}
That periodically checked the time (pausing using Thread.sleep()) and displayed a countdown, and at zero set a flag that the main task periodically checked and ended the game when it saw it was set.
My app was terrible code, but I was learning both Java and Android at the same time as fast as I could, to make an app to help my kid in math class.
Android has changed a lot since then, though.
You can get the time from user and after that you can create one background process which count time everytime and check with user time. If both will match then immediately close your app
Basically i'm using seek bar to show to user how audio playback is going, my visual update is presented in Thread and looks like this :
#Override
public void run() {
while(mUpdating) {
if(mSeekBar != null) {
mSeekBar.setProgress(mAudioPlaybackManager.getCurrentPosition());
}
//Other stuff is updating
}
}
So, for exqample if user plays audio that's 2500 ms in length, this SeekBar max value will be 2500 and it will be updating every ms
This same code working much slower in runOnUiThread, so i'm guessing when progress is changed something like postInvalidate is called
So basically every ms, the seekbar value should be changed. I guess that's the problem here. On my device Samsung J7 it's working smoothly but on Samsung Galaxy S5 it's just stopping and jumping all the time, like if i put this code in runOnUIThread, it would be really slow.
What could i do to make it smoother? Is there another View that i can use for this pirpose?
The things that current SeekBar is doing:
Showing progress of audio basically in while(updating)
When user changes the position of SeekBar audio is starting from that point.
The thread is a worker thread and not UI's thread, you're manipulating UI component within the thread thus blocking the UI's thread.
If you want to update the seekbar, update it in UI's thread. It can be done with UI's thread handler.
For example
private Runnable updateSeekBarTime = new Runnable() {
public void run() {
//get current position
timeElapsed = mediaPlayer.getCurrentPosition();
//set seekbar progress
seekbar.setProgress((int) timeElapsed);
//repeat yourself again in 100 miliseconds
durationHandler.postDelayed(this, 100);
}
};
Executed in
public void play(View view) {
mediaPlayer.start();
while(mUpdating) {
if(mSeekBar != null) {
// post the Runnable (updateSeekBarTime)
durationHandler.postDelayed(updateSeekBarTime, 100);
}
}
}
Also updating component can't simply be done every millisecond, from user perspective, giving a bit pause won't be noticed.
At onCreate, I run a task that repeats every minute. Here how I do it:
myTimer = new Timer();
int delay = 30000; // delay for 30 sec.
int period = 600000; // repeat every 60 sec.
doThis = new TimerTask() {
public void run() {
Log.v("TImer","repeated");
wv.reload();
}
};
myTimer.scheduleAtFixedRate(doThis, delay, period);
All that code is in onCreate. So, when the app goes off from the screen, I can see in logcat that timer steel runs, and will not stop unless the app will be destroyed. So, in onPause of activity I call myTimer.cancel(); but it didnt help. I still can see updates in logcat, even when the app is not on the screen. So, how to stop timerTask?
Here is your code put into my file, with the values delay and period tweaked so I don't have to wait so long. I run the app. I see the messages in LogCat. I press the home button on my Galaxy S3. Then the messages stop in LogCat.
public class MainActivity extends Activity {
Timer myTimer;
TimerTask doThis;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myTimer = new Timer();
int delay = 0; // delay for 30 sec.
int period = 1000; // repeat every 60 sec.
doThis = new TimerTask() {
public void run() {
Log.v("TImer","repeated");
}
};
myTimer.scheduleAtFixedRate(doThis, delay, period);
}
#Override
protected void onPause() {
myTimer.cancel();
super.onPause();
}
}
It could be that since the thread has already been sent out, it runs one last time. In the onLoad set a variable to true, then in the onPause set it to false. Then in your timer task only run your code if the variable is true.
Write to the log outside of the new if statement though. If it is indeed running it just one last time, then that might be your solution. But if it is still running it over and over multiple times after the onPause then don't take my solution.
The actual answer is: The onPause method needs to be defined correctly.
The whole story:
The questioner defined the onPause method wrong. That was the reason for asking this question.
I'm writing this because I spent too much time reading question, answers, code and comments. The real answer was hidden in the last comment.
I'm trying to make a countdown timer in android for use in a small android app. The app will countdown from some number of seconds to 0, upon which it will do some action. I'm using the coundowntimer supplied by android.os.countdowntimer. Here is my code:
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.quizlayout);
new CountDownTimer(30000, 1000) {
TextView tx = (TextView) findViewById(R.id.textView2);
public void onTick(long millisUntilFinished) {
tx.setText("seconds remaining: " + millisUntilFinished / 1000);
}
public void onFinish() {
tx.setText("done!");
}
}.start();
}
However, this countdown timer is really slow. It takes like 3 real-time seconds for the timer to countdown by one second. I wonder what's going on? The code I have above is more or less copied straight from google (CountDownTimer)
Can anyone help me as per why my timer is so slow, and offer a way to speed it up a bit?
(EDIT): I am running this on an emulator, the intel atom x86. I am emulating an android 2.3.3 environment.
According to Android documentation for countdown timer
The calls to onTick(long) are synchronized to this object so that one call to onTick(long) won't ever occur before the previous callback is complete. This is only relevant when the implementation of onTick(long) takes an amount of time to execute that is significant compared to the countdown interval.
Take a look at this example for countdown timer
Countdown timer example
Alternately you can spawn a new thread and just get that thread to sleep for the interval you want and take actions when it wakes or vice versa.
You can also timertask
use a handler that will post the same runnable . this will remove the need for extra threads :
Handler handler=new Handler();
handler.postRunnable(... , 1000) ;
in the runnable , call the postRunnable again for the same handler (and add a condition for when to stop) .
CountDownTimer is not efficient regardless to ui updating performances. For a flawless ui update, it is better to create a custom countdown. I did my own so here it is. It is flawless on my app.
public abstract class CountDown {
int totalTime = 0;
int tickTime = 0;
Thread thread;
boolean canceled = false;
public CountDown(int totalTime,int tickTime){
this.totalTime = totalTime;
this.tickTime = tickTime;
}
public abstract void onTick();
public abstract void onFinish();
public void start(){
thread = new Thread(new Runnable() {
#Override
public void run() {
// Do in thread
canceled = false;
for (int elapsedTime = 0; elapsedTime < totalTime; elapsedTime += tickTime) {
if(!canceled){
onTick();
try {
thread.sleep(tickTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
break;
}
}
if(!canceled){
onFinish();
}
}
});
thread.start();
}
public void cancel(){
canceled = true;
}
}
Remember that every time you have to update your ui, call a runOnUiThread, or else you will have an exception, you are not in a handler and not on ui thread.
Here is how to use it in your code, it is identical to CountDownTimer, so you could just rename lines in your code :
CountDown cDown = new CountDown(10000, 20) {
public void onTick() {
// Do something
}
public void onFinish() {
runOnUiThread(new Runnable() {
#Override
public void run() {
myButton.setImageDrawable(drawable);
}
});
}
};
I know you can seekto() with Mediaplayer, to start at a certain point.
But is there a way to make a track (the audio playing) stop at a certain point? Or would an if statement on a timer loop have to be used?
Doesn't seem possible (correct me if I'm wrong) to do this with media player without resorting to seekto() in a timer loop. However you could try using an AudioTrack in conjunction with setting a notification marker:
AudioTrack.setNotificationMarkerPosition
Sets the position of the notification marker.
and overriding the playback position update listener AudioTrack.OnPlaybackPositionUpdateListener
Interface definition for a callback to be invoked when the playback head position of an AudioTrack has reached a notification marker or has increased by a certain period.
You have to make threat that will trigger getCurrentPosition().
When it will reach stop point, you have to stop MediaPlayer.
public void run() {
while (mp != null && mPosition < mTotal) {
try {
Thread.sleep(500); // you can modify sleep time for better accuracy
if (mp.isPlaying()) {
mPosition = mp.getCurrentPosition();
if (mPosition == mYourStopPoint) { //remember to set mYourStopPoint
mp.stop();
break;
}
}
} catch (InterruptedException e) {
return;
} catch (Exception e) {
return;
}
}
}
Start this Thread in onPreapared callback.
public void onPrepared(MediaPlayer genericPlayer) {
mTotal = mp.getDuration();
new Thread(this).start();
}
Sadly, AudioTrack's position callbacks appear to be fairly seriously broken. http://code.google.com/p/android/issues/detail?id=2563