Android MediaPlayer, SeekBar Exception when change activity - java

I am currently working on an android application with audio playing function. In the play_pause Activity, I have a seekbar and a button to control the audio.
public class PlayerActivity extend AppCompatActivity {
...
...
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
playerButton = (Button) findViewById(R.id.playButton);
playerButton.setOnClickListener(new Button.setOnClickListener() {
if (player != null) {
//load music to player
player.create(PlayerActivity.this, R.raw.music);
seekbar.setMax(player.getDuration());
mTimer = new Timer();
mTimerTask = new TimerTask() {
public void run() {
seekbar.setProgress(player.getCurrentPosition());;
}
};
mTimer.schedule(mTimerTask, 0, 10);
}
});
}
}
When I press the back button without hit the "play" button, my application can change activity successfully. However, once the "play" button has been clicked, I can not change activity. Below is the onDestroy() method.
protected void onDestroy() {
super.onDestroy();
player.release();
}
The Logcat has the error message saying
java.lang.IllegalStateException
at MediaPlayer.getCurrentPosition()
at PlayerActivity.run() //which is the mTimerTask
at java.util.Timer$TimerImpl.run()
Anyone have idea how to solve?

Cancel your Timer onDestroy of Activity too by calling
mTimer.cancle();
Exception raised due to call of player.getCurrentPosition(); after release of Media player instance on destroy of Activity as you timer thread dont stops.

Related

Cancel a CountDownTimer in another activity

I have a CountDownTimer in one activity that I need to cancel in a different activity. The problem I'm having is that the CountDownTimer is not getting cancelled even when I make a call to the endTestTimer() method in another activity. Here is the code for the CountDownTimer:
private long testMillisLeft, questionMillisLeft;
private static CountDownTimer testTimer;
private void startTestTimer()
{
long startTime = DataHolder.getTestTime()*1000; //getTestTime() is the seconds
testTimer = new CountDownTimer(startTime, 1000) {
#Override
public void onTick(long millisUntilFinished) {
testMillisLeft = millisUntilFinished;
updateTestTimeLeft();
}
#Override
public void onFinish() {
Intent intent = new Intent(MainActivity4.this, MainActivity5.class);
startActivity(intent);
}
}.start();
}
public static void endTestTimer()
{
if(testTimer != null)
{
testTimer.cancel();
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main4);
testTimeLeft = (TextView) findViewById(R.id.testTimerTextView);
mathProblem = (TextView) findViewById(R.id.mathProblemTextView);
testTimer = null;
startTestTimer();
}
And here is how I am trying to cancel the same timer in another activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainActivity4.endTestTimer();
}
A comment on this related post says that the static CountDownTimer creates memory leak. If that's the case, how can I call the endTestTimer() method in another activity and have it cancel the timer? Alternatively, how can I access the testTimer CountDownTimer directly and call .cancel() on it in another activity if it can't be static?
Looking into Android lifecycles led me to this post and this post, both of which I found very helpful. To my understanding, when the user hits the back button, the onDestroy() method is called for the currently-displayed activity, and then the onCreate() method is called for the activity to be displayed. So, to cancel the timers on MainActivity4 (the current activity in my case), I added this code to its class file:
#Override
protected void onDestroy() {
testTimer.cancel();
questionTimer.cancel();
super.onDestroy();
}
Now, when the user backs out of MainActivity4 (regardless of what other activity that takes them to) and onDestroy() is called automatically, both timers are cancelled and then MainActivity4 is destroyed. This seems to work fine for me, but I'm not sure if there are any downsides to doing it this way, so please let me know if there are.

My android App stop playing sound on button tap after some time

Hello there, i have just created a basic android app which plays different musics on button tap..
The app just worked fine for the first couple of seconds but when i just keep on tapping and tapping , at some point it stops playing the music and just crashed...
I am unable to figure out what the problem is ..Please help me make it work..
Thanks.
Here is my code :-
public class MainActivity extends AppCompatActivity {
MediaPlayer mediaPlayer;
public void PlayMusic(View view)
{
int ID = view.getId();
String NameID = view.getResources().getResourceEntryName(ID);
int sound= getResources().getIdentifier(NameID,"raw","com.example.pickachu.mypatatap");
mediaPlayer = MediaPlayer.create(this,sound);
mediaPlayer.start();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
The sound is not playing after multiple taps because you must be getting IllegalStateException because you are not releasing the Mediaplayer object and Mediaplayer Lifecycles are not managed properly when you tap multiple times.
You can use setOnCompletionListener(MediaPlayer.OnCompletionListener listener) to release mediaPlayer after completion of sound as:
mediaPlayer = MediaPlayer.create(this,sound);
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mp.reset();
mp.release();
mediaplayer = null;
}
});
mediaPlayer.start();
pass uri instead string
mediaPlayer= MediaPlayer.create(this, Uri.parse(Environment.getExternalStorageDirectory().getPath()+ "/Music/music.mp3"));
mediaPlayer.setLooping(true);
mpintro.start();

Updating TextView with timer onTick after leaving and returning to activity

I'm writing a workout app and am trying to implement a rest timer in the Train activity. CountDownTimer located within Train and is called when the user presses a start button.
public CountDownTimer createTimer(long timerDuration) {
Log.d("new timer duration:", "value: " + timerDuration);
return new CountDownTimer(timerDuration, 1000) {
#Override
public void onTick(long millisUntilFinished) {
int progress = (int) (millisUntilFinished / 1000);
secondsLeftOnTimer = progress; // update variable for rest of app to access
// Update the output text
breakTimerOutput.setText(secondsToString(progress));
}
#Override
public void onFinish() { // Play a beep on timer finish
breakTimerOutput.setText(secondsToString(timerDurationSeconds));
playAlertSound(); // TODO: Fix the delay before playing beep.
}
}.start();
}
The timer works, as long as the user stays in the Train activity. If you switch to another activity, the timer continues to run in the background (the beep still occurs), which is what I want. If you go back to the Train activity, however, the breakTimerOutput TextView is no longer updated by onTick.
How can I "reconnect" breakTimerOutput to onTick when the user re-enters the Train activity?
Here is the full code for the activity, just in case.
I would like to suggest to keep the timer inside a Service and use BroadcastReceiver to receive the tick to update the TextView in your TrainActivity.
You need to start the CountDownTimer from the Service. So in the onCreate method of your Service you need to initialize a LocalBroadcastManager.
broadcastManager = LocalBroadcastManager.getInstance(this);
So on each tick on your timer (i.e. onTick method), you might consider calling a function like this.
static final public String UPDATE_TIME = "UPDATE_TIME";
static final public String UPDATED_TIME = "UPDATED_TIME";
public void updateTextView(String time) {
Intent intent = new Intent(UPDATE_TIME);
if(time != null)
intent.putExtra(UPDATED_TIME, time);
broadcastManager.sendBroadcast(intent);
}
Now in your TrainActivity create a BroadcastReceiver.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.copa);
receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String time = intent.getStringExtra(YourService.UPDATED_TIME);
// Update your TextView here.
}
};
}
And additionally you need to register and unregister the BroadcastReceiver.
#Override
protected void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver((receiver),
new IntentFilter(YourService.UPDATE_TIME)
);
}
#Override
protected void onStop() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
super.onStop();
}

How do I correctly stop a Media Player in a CountDown Timer when the activity is Paused?

I can successfully cancel the Countdown Timer before it finishes when the activity is paused. However, the media player implemented in the OnTick method of the canceled timer doesn't stop for 5-8 seconds. What would be the correct way to disable the sound of the media player in the CountDown Timer when the activity is paused?
private Button soundOn;
private Button soundOff;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_level_one);
SharedPreferences savedSoundToggle = getSharedPreferences(PREFS_SOUNDTOGGLE, 0);
final SharedPreferences.Editor soundEditor = savedSoundToggle.edit();
nSound = savedSoundToggle.getInt("SoundOnorOff", 0);
if(nSound == 1){
soundOff.setEnabled(false);
soundOff.setVisibility(View.INVISIBLE);
soundOn.setEnabled(true);
soundOn.setVisibility(View.VISIBLE);
}
else if( nSound == 0){
soundOn.setEnabled(false);
soundOn.setVisibility(View.INVISIBLE);
soundOff.setEnabled(true);
soundOff.setVisibility(View.VISIBLE);
}
}
public void OneGame(){
final MediaPlayer intro = MediaPlayer.create(getApplicationContext(), R.raw.minicycleaudio);
final MediaPlayer go = MediaPlayer.create(getApplicationContext(), R.raw.flashgoaudio);
getReadyTimer = new CountDownTimer(6000, 1000) {
public void onTick(long millisUntilFinished) {
getReadyTimeText.setText("" + millisUntilFinished / 1000);
if(soundOn.isEnabled()){
intro.start();
}
if(soundOff.isEnabled()){
if(intro.isPlaying()){
intro.pause();
}
}
}
public void onFinish() {
if(intro.isPlaying()) {
intro.stop();
intro.release();
}
if(soundOn.isEnabled()) {
go.start();
}
#Override
public void onPause() {
super.onPause();
getReadyTimer.cancel();
}
}
I'm almost 100% positive that the Countdown timer becomes canceled when the activity is paused because the media player in the onFinish() function is never called. Why does the media player in the onTick method keep playing?
I figured it out: To cancel a media player in a CountDown Timer, when the activity is paused... you must declare the MediaPlayer as static, not final.
static MediaPlayer intro;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_level_one);
getReadyTimeText = (TextView) findViewById(R.id.levelonetimerText);
public void OneGame(){
intro = MediaPlayer.create(getApplicationContext(), R.raw.minicycleaudio);
}

Seekbar handler Null Exception

In the mainactivity I have create a handler as instance variable:
SeekBar seek_bar;
MediaPlayer player;
Handler seekHandler = new Handler();
Then I have the following two methods in the MainActivity to update the seekbar as the audio plays in media player:
public void getInit() {
seek_bar = (SeekBar) findViewById(R.id.seek_bar);
}
Runnable run = new Runnable() {
#Override
public void run() {
seekUpdation(); **//Exception comes here while closing the app**
}
};
public void seekUpdation() {
seek_bar.setProgress(mMediaPlayer.getCurrentPosition());
seekHandler.postDelayed(run, 1000);
}
The problem I am facing is that when the audio is running and user closes the application using device back buttton. I get NullPointerException. The destroy method of activity is:
#Override
protected void onDestroy() {
super.onDestroy();
if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
}
}
Remove any queued messages/callbacks from the handler in your onDestroy. My bet is that its running the last message after onDestroy is called.

Categories