I am working on music player app where I am doing streaming of music using MediaPlayer. Streaming is working fine but now I want If user press back button of the activity then it should stop preparing & release media player immediately.
Currently when it is preparing and if activity is being destroyed then I am releasing the MediaPlayer like this but when it release it then it hangs the application & show ANR.
#Override
protected void onDestroy() {
super.onDestroy();
if(mediaPlayer!=null) {
mediaPlayer.stop();
mediaPlayer.release();
}
}
I am initializing MediaPlayer like below
mediaPlayer = null;
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource("http://www.samisite.com/sound/cropShadesofGrayMonkees.mp3");
Now I want when it start preparing then on back press when I am releasing in onDestory then it should not hang the app & release the media player smoothly.
Please help me what is the best way to do this. Thanks in advance
mediaPlayer.prepareAsync();
Check the answer here. And the MediaPlayer State Diagram from android code.
Edit:
#TGMCians I showed him the provided link, So, if the playback is still not ready or preparing, he can not call stop() until it's called onPrepared. I'm not sure that onPrepared keep called after the app onDestroy called. So, The full snip I think is:
private boolean mPrepared = false;
private boolean mCancel = false;
public void onPrepared(MediaPlayer player){
mPrepared = true;
if(mCancel){
player.release();
mPrepared = false;
mCancel = false;
//nullify your MediaPlayer reference
mediaPlayer = null;
}
}
private void cancelMedia(){
mCancel = true;
}
#Override
protected void onDestroy() {
super.onDestroy();
cancelMedia();
if (mediaPlayer != null && mPrepared) {
mediaPlayer.stop();
mediaPlayer.release();
mPrepared = false;
mediaPlayer = null;
}
}
Related
I'm trying to pause and resume VideoView with MediaPlayer in activity onPause() and onResume() methods, but in onResume() method MediaPlayer throws java.lang.IllegalStateException. I didn't release MediaPlayer but I think MediaPlayer automatically released after activity paused.
How should I handle it?
private MediaPlayer mediaPlayer;
void prepareVideo() {
videoView = new VideoView(context.getApplicationContext());
String path = "android.resource://" + getPackageName() + "/" +
R.raw.my_video;
videoView.setVideoPath(path);
}
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer = mp;
mediaPlayer.start();
}
});
#Override
protected void onResume() {
super.onResume();
if (mediaPlayer != null) {
mediaPlayer.start();
}
}
#Override
protected void onPause() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
}
super.onPause();
}
The exception:
Caused by: java.lang.IllegalStateException
at android.media.MediaPlayer._start(Native Method)
at android.media.MediaPlayer.start(MediaPlayer.java:1194)
at co.myapp.app.reborn.myappTestActivity.onResume(myappTestActivity.java:370)
at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1259)
at android.app.Activity.performResume(Activity.java:6347)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3110)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3152)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1400)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5530)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:734)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:624)
Take a look at the mediaplayer state diagram on android documentation
MediaPlayer state diagram
According to the diagram you have to call setDataSource() and prepare() before calling start().
Probably something wrong happened before. Your logcat should point you in the right direction.
My guess is that your mediaplayer is not in paused state but in stopped state. So you have to call prepare and then start, not just start.
Unfortunately in this way your playback will restart from scratch.
You can use seek command for resuming a position saved during the activity pausing.
We just need to implement MediaPlayer.OnSeekCompleteListener interface and set MediaPlayer in onSeekComplete method.
private MediaPlayer mediaPlayer;
#Override
public void onSeekComplete(final MediaPlayer mp) {
mediaPlayer = mp;
}
I was facing this issue yesterday and I'd like to share my experience facing this issue, the root cause and the fix in my particular issue; this might be helpful for someone else.
This is what I found in my particular issue.
I´m running MediaPlayer in Fragment
When the phone goes to sleep (black screen), in the fragment-life-cycle the stop() function is call.
MediaPlayer recommends to release() MediaPlayer resources on STOP state.
When the phone resumes, it complains with illegal-state-exception because there are no MediaPlayer resource available, remember it was released in the STOPE state.
So, to fix it. I overrided start() state and I got another instance of MediaPlayer if it was null at that specific point in time. START state is called at resume time.
sample code
public class xMediaPlayer extends MediaPlayer
{
private static xMediaPlayer instance = null;
public static xMediaPlayer getInstance( )
{
if( instance == null )
instance = new xMediaPlayer( );
return instance;
}
}
Override start() on fragment
#Override
public void onStart() {
super.onStart();
mMediaPlayer = xMediaPlayer.getInstance( );
}
In my project I am trying to make a media player which plays a shoutcast stream. Everything seemed to be working well until I pressed the back button on my device, which I think stops the activity and causes the device to recreate the activity when launched again. The problem is , when the activity is recreated , I lose the control of the mediaplayer and a new mediaplayer is created.
I need to be able to have the mediaplayer's control back at that point. How is it possible?
This part of code belongs to onCreate
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button) findViewById(R.id.button);
if (mediaPlayer == null){
mediaPlayer = new MediaPlayer();
try {
mediaPlayer.setDataSource(getString(R.string.yayin));
} catch (Exception e) {
e.printStackTrace();
}
mediaPlayer.prepareAsync();
}
if(!isPlaying){
btn.setBackgroundResource(R.drawable.oynat);
}
else{
btn.setBackgroundResource(R.drawable.durdur);
}
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(!isPlaying){
playOnReady();
isPlaying = true;
btn.setBackgroundResource(R.drawable.durdur);
}
else{
mediaPlayer.reset();
isPlaying = false;
btn.setBackgroundResource(R.drawable.oynat);
}
}
});
This part of code belongs to the function playOnReady()
private void playOnReady(){
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
}
Take a look at the Android Activity lifecycle flowchart: https://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle
You need to account for the path where onPause is called when you leave the Activity and then onResume is called when you enter it again. The solution for you could be as simple as moving some/all of your code from onCreate into onResume.
Using this tutorial I have managed to build a service which handles the mediaplayer that I can have the control of anytime I need.
I have two activities a main activity which has Recyclerview and a detailedActivity which is launched every time the user clicks on one of the items of the Recyclerview. The detailedActivity has a mediaplayer component that is being created everytime a detailedActivity is created. Now in the onDestroy method I always free the resources taken by the mediaPlayer by this code:
#Override
protected void onDestroy() {
if (mMediaPlayer != null) {
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();}
mMediaPlayer.release();
mMediaPlayer=null;
}
super.onDestroy();
}
The app freezes for a while every time I click the back button while the mediaplayer is still preparing. The message that I get in the logcat is this:
I/Choreographer: Skipped 112 frames! The application may be doing too much work on its main thread.
So this freezing only happens if I destroyed the activity while it is preparing but if it is already in the prepared state it won't happen. I use prepreAsync to fetch the media from the internet.
Thanks. Any help is highly appreciated. I have been stuck in this problem for days!
OK. I've kind of worked around the problem. I am writing this for anyone who might encounter the same situation as I did. I made two boolean flags in the scope of the class like this:
boolean prepared = false;
boolean cancel = false;
After that in the onpreapred method I set prepared to true.
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
prepared = true;
}
});
In the onDestroy method I check whether the mediaplayer is already prepared or not if prepared I release it from the method its self.
#Override
protected void onDestroy() {
if (mMediaPlayer != null) {
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();
}
if (prepared) {
mMediaPlayer.release();
mMediaPlayer = null;
}
cancel = true;
}
super.onDestroy();
}
Otherwise, I set cancel to true and implement on OnBufferingUpdateListener interface and override its method and release the mediaplayer from there.
#Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
if (cancel) {
mp.reset();
mp.release();
mMediaPlayer = null;
Log.i("msg", " mp released");
}
}
Try to remove rechecking if mediaplayer is running or not.
#Override
protected void onDestroy() {
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.release();
}
super.onDestroy();
}
If it still happens, try to remove checking mediaplayer in your onDestroy().
-- UPDATE --
This could be related with this bug:
https://code.google.com/p/android/issues/detail?id=959
This could be a help:
Android MediaPlayer reset freezes UI
I'm trying to use a MediaPlayer instance to play several audio files individually, in response to various sensor events.
I've found that when I load up the clip to be played right before calling MediaPlayer.start(), the audio clip will play fine. However, the application takes a major performance hit. Ideally, each audio clip should be loaded into the MediaPlayer immediately after the last one was played, leaving the MediaPlayer ready to start playback the instant the SensorEvent comes in.
I would expect this to be simple, but now that I made the change the audio just doesn't play. PlayAudioClip() is definitely still being called as expected, but something is going wrong after that. No errors are thrown, so I don't think the MediaPlayer is changing state, but could something be interfering with in the time that it's waiting to play?
Here is a simplified version of my code:
public class MainActivity extends Activity implements SensorEventListener {
private Random numGenerator;
private SensorManager manager;
private Sensor accelerometer;
private MediaPlayer mediaPlayer;
private Uri[] audioClips;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initVariables();
prepareNextAudioClip(); //load first audioClip
}
#Override
public void onSensorChanged(SensorEvent event) {
if(conditionsRight()){
playAudioClip();
}
}
}
private void playAudioClip() {
mediaPlayer.start();
prepareNextAudioClip();
}
private void prepareNextAudioClip() {
try {
mediaPlayer.reset();
Uri audioClip = audioclips[(int) Math.floor(numGenerator.nextDouble()*audioClips.length)];
mediaPlayer.setDataSource(this, audioClip);
mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
}
}
//Code below here isn't very important... handling setup and teardown
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {}
protected void onResume() {
super.onResume();
manager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_UI);
}
private void initVariables() {
audioClips = new Uri[]{
Uri.parse("android.resource://com.example.afraidofflying/" + R.raw.audio1),
Uri.parse("android.resource://com.example.afraidofflying/" + R.raw.audio2),
Uri.parse("android.resource://com.example.afraidofflying/" + R.raw.audio3)
};
numGenerator = new Random();
mediaPlayer = new MediaPlayer();
manager = (SensorManager)getSystemService(SENSOR_SERVICE);
accelerometer = manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
if(null == accelerometer) finish();
}
protected void onPause() {
super.onPause();
manager.unregisterListener(this);
}
protected void onDestroy(){
mediaPlayer.release();
mediaPlayer = null;
}
}
PS: This has all been assuming I'll only use one instance of MediaPlayer but I'd also like input on if you think using multiple MediaPlayers and delegating each of them 1 audio clip would be advisable. My intuition is no because for my purposes I'd have to use 10-20 MediaPlayers, but it would be good to hear outside perspectives on it.
It's because you're resetting player right after starting playback.
private void playAudioClip() {
mediaPlayer.start(); //starting playback
prepareNextAudioClip(); //reset
}
if you want to play files in queue, than you can use one instance. But if you have to play several files simultaneusly, then you need to have several media player instances.
I think you have to look at subtle points regarding using Mediaplayer class
In your code you used:
initVariables();
prepareNextAudioClip(); //load first audioClip
initVariables() seems ok, Now lets see prepareNextAudioClip()
...
mediaPlayer.reset();
...
...
mediaPlayer.prepare();
The above code seems to corrupt Mediaplayer state machine. Please refer to http://developer.android.com/reference/android/media/MediaPlayer.html for details on using new, prepare,reset. It is better to write defensive MediaPlayer code using Errorlistener
I have an android app with a button that plays a sound. the code for playing the sound:
if (mp != null)
{
mp.release();
}
mp = MediaPlayer.create(this, R.raw.match);
mp.start();
mp is a field in the activity:
public class Game extends Activity implements OnClickListener {
/** Called when the activity is first created. */
//variables:
MediaPlayer mp;
//...
The app runs ok, but after clicking the button about 200 times on the emulator, app crashed and gave me this error https://dl.dropbox.com/u/5488790/error.txt (couldn't figure how to post it here so it will appear decently)
i am assuming this is because the MediaPlayer object is consuming up too much memory, but isn't mp.release() supposed to take care of this? What am i doing wrong here?
If you are attaching a sound effect to a button, MediaPlayer in general is far too heavyweight for this operation. You're getting unnecessary latency each time just to load up the sound data into memory. You should look at using SoundPool instead.
In either case, there is no valid reason to release and re-create the MediaPlayer each time. If you decide to use MediaPlayer, control the single instance you have with the button clicks.
MediaPlayer mp;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Other init code
//Create the player this way so it doesn't auto-prepare
mp = new MediaPlayer();
AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.match);
mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
afd.close();
}
public void onDestroy() {
super.onDestroy();
//Release it only when no longer needed
mp.release();
mp = null;
}
public void onButtonClick(View v) {
if (mp.isPlaying()) {
mp.stop();
}
//Play the sound
mp.prepare();
mp.start();
}
Hope that Helps, but again, I would highly recommend using SoundPool instead if this sound is just a short effect.
It looks like your code should work, but obviously release() isn't really releasing everything.
Maybe it's because you have to reload R.raw.match every time you want to play the sound. If R.raw.match is just a short sound effect, then you might want to consider using SoundPool instead.
If you use SoundPool you only have to load R.raw.match once which may prevent the memory issues.
This tutorial has a good example on how to use it: http://www.vogella.com/articles/AndroidMedia/article.html#tutorial_soundpool
You pretty much just make one instance of SoundPool then load the sound once and play it when you need it.
Hope this helps!
Edit
If you want to use MediaPlayer...
public class Blah extends Activity implements OnClickListener {
MediaPlayer mp;
#Override
public void onCreate(Bundle b)
{
// blah blah
mp = MediaPlayer.create(R.raw.match);
// blah blah
}
#Override
public void onClick(View v)
{
if (v.getId() == yourButtonID)
{
// play sound from beginning
mp.seekTo(0);
mp.start();
}
}
}
This way you only create one instance and whenever you want to play it, you just rewind it to the beginning then play.
Try
if (mp != null)
{
mp.release();
}
mp = MediaPlayer.create(this, R.raw.match);
mp.prepare(); // not needed
mp.start();
Good luck!!
if you hold the MediaPlayer, release it at the end of the activity
#Override
void onDestroy() {
if (mMediaPlayer != null) {
mMediaPlayer.release();
mMediaPlayer = null;
}
super.onDestroy();
}