Is my code sufficient to prevent MediaPlayer leakage? - android

In my Activity I have the following:
private Set<MediaPlayer> mediaPlayers;
public void onSomeEventInMyActivity()
{
// play sound
MediaPlayer mediaPlayer = MediaPlayer.create(this, R.raw.my_sound);
mediaPlayers.add(mediaPlayer);
mediaPlayer.setOnCompletionListener(new OnCompletionListener()
{
#Override
public void onCompletion(MediaPlayer mp)
{
mp.release();
mediaPlayers.remove(mp);
}
});
mediaPlayer.start();
}
#Override
protected void onStart()
{
super.onStart();
mediaPlayers = new HashSet<MediaPlayer>();
}
#Override
protected void onStop()
{
super.onStop();
for (MediaPlayer mediaPlayer : mediaPlayers)
{
if (mediaPlayer.isPlaying())
{
mediaPlayer.stop();
}
mediaPlayer.release();
}
}
Is this code sufficient or will it lead to MediaPlayer leakage? Are my implementations of onStop and onStart necessary, or can I just rely on calling release in onCompletion?
I did my code this way because I assume onStop() could be called while a MediaPlayer is playing, so I need to call release because onCompletion won't be called yet. I'm just guessing that this is right, so correct me if I am wrong.
I also read that onStop is not called in low-memory situations - what to do then?

An onStop() routine is needed if the mediaPlayer is expected to stop when the activity becomes invisible. Otherwise, the mediaPlayer goes on playing. On older OSs, Gingerbread and earlier, the activity can execute onPause() - say, when a phone call arrives - and, in extreme circumstances, be destroyed without ever executing onStop(). I don't know what would happen to a running mediaPlayer then. However, if there is a phone call coming in, it might be an idea to stop the mediaPlayer in onPause()! Later OSs always pass through onStop() before destroying the Activity. Calling mp.release() on the mediaPlayer after stopping it, in either onPause() or onStop(), is correct.
It's also desirable to remove the reference to the player held in mediaPlayers, which doesn't happen in onStop() above. Something like:
#Override public void onCompletion(MediaPlayer mp) {
mp.stop(); // It's always safe to call stop()
mp.release(); // release resources internal to the MediaPlayer
mediaPlayers.remove(mp); // remove reference to MediaPlayer to allow GC
}
and then
#Override public void onPause() {
for (Object mediaPlayer : mediaPlayers.toArray()) {
onCompletion((MediaPlayer) mediaPlayer); // stop, release, and free for GC, each mp.
}
super.onPause();
}
(I originally had for (Object mediaPlayer : mediaPlayers) {} in the above code but omfeddf345mnof32nisd45fgoq2t pointed out that I would be modifying a set while iterating over it. Thanks for the correction!)

Only callback guaranted to be called is onPause(), so you may leak this media player in some situations. In case stopping player on activity pause is not acceptable you should use service, and watch for certain events ( like incoming phone call etc )

Related

Android MediaPlayer class throws java.lang.IllegalStateException after activity resumed

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( );
}

MediaPlayer mp3 won't resume after pause, known solutions not working

My activity is playing mp3 file while is active, and my intention is to pause it while app takes user to another activity and resume when this activity is again active. Solution that was here around seems to be the right one but unfortunately id doesn't work, the audio file every time starts from beginning. My code is obvious:
private int length;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
mediaPlayer = new MediaPlayer();
mediaPlayer = MediaPlayer.create(this, R.raw.bensound_thejazzpiano);
mediaPlayer.setLooping(true);
mediaPlayer.setVolume(0.4f,0.4f);
mediaPlayer.start();
}
#Override
protected void onPause() {
super.onPause();
mediaPlayer.stop();
length = mediaPlayer.getCurrentPosition();
}
#Override
protected void onResume() {
super.onResume();
mediaPlayer.seekTo(length);
mediaPlayer.start();
}
I would also be satisfied with methods that mutes sound and restores the volume after goin back to activity (and muted tune can go on in background), but replacing start/stop methods with setVolume also doesn't provide to any results...
Maybe there are wrong methods I've overridden?
You have to "pause"(not stop) the mediaplayer in onPause, then start in onResume. That worked for me.

Audio is playing twice in Android Media Player

In my application I'm having a method responsible for playing file placed under raw directory. But when Ever I call that function in my onResume() method, sound is played twice. Even I have googled and tried different solutions. even by checking mediaPlayer.isPlaying() and then stopping the MediaPlayer instance but still didn't get any help.
private void EnglishSound(){
if(mediaPlayer1!=null){
if(mediaPlayer1.isPlaying()){
mediaPlayer1.stop();
}
mediaPlayer1.reset();
mediaPlayer1.release();
}
mediaPlayer1 = MediaPlayer.create(this, R.raw.p012);
mediaPlayer1.start();
}
public void onResume() {
super.onResume();
EnglishSound();
}
and EnglishSound() is called no where else in the whole activity. Even have tried debugging but it never enters the if block containing isPlaying().
Try to release onPause()
public void onPause() {
super.onPause();
if(mediaPlayer1 != null)
mediaPlayer1.release();
}

Why does my MediaPlayer stop working if I "interrupt" it?

I want my button to be "spammable". This means that if I tap on the button repeatedly the MediaPlayer starts all over again.
firstButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (firstTextView.getText().equals("Hello world!")) {
firstTextView.setText("You clicked!");
} else {
firstTextView.setText("Hello world!");
}
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
mediaPlayer.start();
}
});
When I interrupt the MediaPlayer when it is playing, it stops and never starts again. Why?
EDIT: The problem is that I called stop(). Thanks for pointing that out.
As per the documentation, you need to re-prepare the MediaPlayer (emphasis mine):
Once in the Stopped state, playback cannot be started until
prepare() or prepareAsync() are called to set the MediaPlayer object
to the Prepared state again.
Seems you are stopping the player because you are calling mediaPlayer.stop() this makes the MediaPlayer state to go in Stopped state. It will continue to play again when you call prepare() or prepareAsync() and has its preparation callback fired to start the playing media.

MediaPlayer stops working after 30 uses?

I'm trying to write a function to play a short sound (in /res/raw) in my program, called at effectively random times throughout the program. So far I have this function:
public void playSound() {
MediaPlayer mp = new MediaPlayer();
mp = MediaPlayer.create(this, R.raw.ShortBeep);
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
mp.setLooping(false);
mp.start();
}
It works fine for awhile, but after exactly 30 plays of the sound, it stops making sound.
According to the Docs
... failure to call release() may cause subsequent instances of MediaPlayer objects to fallback to software implementations or fail altogether.
When you are done with it call mp.release() so that it can release the resources. I don't know what the limit is and I'm sure it depends on many factors. Either way you should be calling this function on your MediaPlayer object, especially if it will be used more than once.
I've just solved the exact same problem, but I'm using Xamarin. I ended up changing from holding on to a MediaPlayer instance for the lifetime of the activity to creating an instance each time I want to play a sound. I also implemented the IOnPreparedListener and IOnCompletionListener.
Hopefully you can get the idea despite it being C# code
public class ScanBarcodeView :
MvxActivity,
MediaPlayer.IOnPreparedListener,
MediaPlayer.IOnCompletionListener
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.ScanBarcodeView);
((ScanBarcodeViewModel) ViewModel).BarcodeScanFailed += (sender, args) => PlaySound(Resource.Raw.fail);
((ScanBarcodeViewModel) ViewModel).DuplicateScan += (sender, args) => PlaySound(Resource.Raw.tryagain);
}
private void PlaySound(int resource)
{
var mp = new MediaPlayer();
mp.SetDataSource(ApplicationContext, Android.Net.Uri.Parse($"android.resource://com.company.appname/{resource}"));
mp.SetOnPreparedListener(this);
mp.SetOnCompletionListener(this);
mp.PrepareAsync();
}
public void OnPrepared(MediaPlayer mp)
{
mp.Start();
}
public void OnCompletion(MediaPlayer mp)
{
mp.Release();
}
}
So, each time I want a sound to be played I create a MediaPlayer instance, so the data source, tell it that my Activity is the listener to Prepared and Completion events and prepare it. Since I'm using PrepareAsync I don't block the UI thread. When the media player is prepared the Start method on the MediaPlayer is called, and when the sound has finished playing the MediaPlayer object is released.
Before I made these changes I would get to 30 sounds played and it would all stop working. Now I've gone way past 30, also multiple sounds can be played simultaneously.
Hope that helps.

Categories

Resources