IllegalStateException calling MediaPlayer.reset()

2020-02-26 04:37发布

问题:

The documentation for the Android MediaPlayer shows that there are no invalid states for the reset() call: http://developer.android.com/reference/android/media/MediaPlayer.html#Valid_and_Invalid_States (the invalid states are listed as {}, or "none.").

However, I have seen an IllegalStateException thrown while calling reset():

java.lang.IllegalStateException
at android.media.MediaPlayer._reset(Native Method)
at android.media.MediaPlayer.reset(MediaPlayer.java:1061)
at com.example.android.player.AsyncPlayer$AsyncHandler.handleMessage(AsyncPlayer.java:654)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.os.HandlerThread.run(HandlerThread.java:60)

Is the documentation incorrect?

回答1:

Hard to say without seeing your code but I think you may be calling reset() after you call release() ?

The documentation states

When a MediaPlayer object is just created using new or after reset() is called, it is in the Idle state; and after release() is called, it is in the End state. Between these two states is the life cycle of the MediaPlayer object.

You may be calling reset outside of the valid life cycle.



回答2:

I ran into your issue, Skyler.

You are correct. The documentation shows no invalid states for mediaPlayer.reset(), but that isn't the first inaccuracy in the documentation.

What I noticed is that the list of VALID states doesn't say "Any"; it lists every specific state except for two: Preparing and End.

I experimented, but could not cause IllegalStateException to be thrown in my attempts to call release() while the MediaPlayer is hopefully in the Preparing state (using prepareAsync()). I won't guarantee it doesn't happen, but I couldn't make it happen. What I did see in that instance were the following log messages:

04-11 11:41:54.740: E/MediaPlayer(4930): error (1, -2147483648)
04-11 11:41:54.748: E/MediaPlayer(4930): Error (1,-2147483648)

Yes, both error messages appear, one right after the other--one with lowercase "error" and one with uppercase "Error," but no Exception is thrown.

However, if I call reset() after release() then I get the error:

04-11 11:45:05.232: E/AndroidRuntime(5046): FATAL EXCEPTION: main
04-11 11:45:05.232: E/AndroidRuntime(5046): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.android.helloandroid/com.android.helloandroid.HelloAndroidActivity}: java.lang.RuntimeException: java.lang.IllegalStateException
04-11 11:45:05.232: E/AndroidRuntime(5046):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1696)
04-11 11:45:05.232: E/AndroidRuntime(5046):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1716)
04-11 11:45:05.232: E/AndroidRuntime(5046):     at android.app.ActivityThread.access$1500(ActivityThread.java:124)
04-11 11:45:05.232: E/AndroidRuntime(5046):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:968)
04-11 11:45:05.232: E/AndroidRuntime(5046):     at android.os.Handler.dispatchMessage(Handler.java:99)
04-11 11:45:05.232: E/AndroidRuntime(5046):     at android.os.Looper.loop(Looper.java:123)
04-11 11:45:05.232: E/AndroidRuntime(5046):     at android.app.ActivityThread.main(ActivityThread.java:3806)
04-11 11:45:05.232: E/AndroidRuntime(5046):     at java.lang.reflect.Method.invokeNative(Native Method)
04-11 11:45:05.232: E/AndroidRuntime(5046):     at java.lang.reflect.Method.invoke(Method.java:507)
04-11 11:45:05.232: E/AndroidRuntime(5046):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
04-11 11:45:05.232: E/AndroidRuntime(5046):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
04-11 11:45:05.232: E/AndroidRuntime(5046):     at dalvik.system.NativeStart.main(Native Method)
04-11 11:45:05.232: E/AndroidRuntime(5046): Caused by: java.lang.RuntimeException: java.lang.IllegalStateException
04-11 11:45:05.232: E/AndroidRuntime(5046):     at com.android.helloandroid.HelloAndroidActivity.crashMediaPlayer(HelloAndroidActivity.java:423)
04-11 11:45:05.232: E/AndroidRuntime(5046):     at com.android.helloandroid.HelloAndroidActivity.onCreate(HelloAndroidActivity.java:87)
04-11 11:45:05.232: E/AndroidRuntime(5046):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
04-11 11:45:05.232: E/AndroidRuntime(5046):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1660)
04-11 11:45:05.232: E/AndroidRuntime(5046):     ... 11 more
04-11 11:45:05.232: E/AndroidRuntime(5046): Caused by: java.lang.IllegalStateException
04-11 11:45:05.232: E/AndroidRuntime(5046):     at android.media.MediaPlayer._reset(Native Method)
04-11 11:45:05.232: E/AndroidRuntime(5046):     at android.media.MediaPlayer.reset(MediaPlayer.java:1112)
04-11 11:45:05.232: E/AndroidRuntime(5046):     at com.android.helloandroid.HelloAndroidActivity.crashMediaPlayer(HelloAndroidActivity.java:421)
04-11 11:45:05.232: E/AndroidRuntime(5046):     ... 14 more

So The Modern Ink's guess was correct. MediaPlayer.reset() throws IllegalStateException in the End state (which occurs after release() is called).

In my case, I discovered I was calling release() on onPause(), but did nothing to initialize the MediaPlayer again in onResume(). Hence, it was in the End state when I called reset();

Per http://developer.android.com/reference/android/media/MediaPlayer.html,

Once the MediaPlayer object is in the End state, it can no longer be used and there is no way to bring it back to any other state.

That means you need to create the MediaPlayer all over again, starting with mediaPlayer = new MediaPlayer() or one of the mediaPlayer.onCreate() methods. Or be careful about when you call release().



回答3:

Apparently the documentation for the Android MediaPlayer is not right about no invalid state for reset(). Below's what happen when I experienced it:

In my PlayerActivity.java code, I set my MediaPlayer as static so that I can use it in my home activity:

public class PlayerActivity extends Activity {
....

public static MediaPlayer mp;
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Mediaplayer
    if(mp == null) {
        mp = new MediaPlayer();
    }
    ....
}
/**
 * Function to play a song
 * @param songIndex - index of song
 * */
public void  playSong(int songIndex){
    // Play song
    try {
        if(mUpdateTimeTask != null)
            mHandler.removeCallbacks(mUpdateTimeTask);
        mp.reset();
            // the song path is get from internet
    mp.setDataSource(songsList.get(songIndex).get("songPath"));
    mp.prepareAsync();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (IllegalStateException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
...
}

In my home activity, I release the player before closing the app:

public class TuoiTreAppActivity extends TabActivity {
    ...

    @Override
    public void onDestroy(){
        if(PlayerActivity.mp != null) {
        PlayerActivity.mp.release();
        }   
        super.onDestroy();

    }
    ...

}

So, when I launch the app for the first time and start playing a song. The reset() function runs well without error. But when I hit the back button to close the app and launch it for the second time, the IllegalStateException occurs when passing the reset() function.

I also discovered the cause when debugging. As the first time running the app, the player is null so it is initialized in the onCreate() function of PlayerActivity.java. But the player is not release itself to null after the app was closed. So it is not initialized again when re-opening for the second time. That's the reason why IllegalStateException occurs when passing reset() function. So, to solve this problem, I have to set the player to null before closing the app:

@Override
public void onDestroy(){
    if(PlayerActivity.mp != null) {
        PlayerActivity.mp.release();
        // Set the MediaPlayer to null to avoid IlLegalStateException 
            // when call mp.reset() after launching the app again
        PlayerActivity.mp = null;
    }

    super.onDestroy();

}


回答4:

Two ways this can happen:

  1. As mentioned before if you reset after release, you will get an error this is because release will dispose of system resources and thus reset is an invalid command.
  2. If you try to use your mediaPlayer object after release you will also get an exception. This is because you need to recreate the object and request system resources again.

The right order to get rid of your media player is to release it, and set it to null. This order is important as you will have memory leaks otherwise.

Here is a quick example:

    mediaPlayer.release();
    mediaPlayer = null;

Then when your user presses a button, say play, you will have to recreate the mediaplayer:

public void createMediaPlayer() {
    mediaPlayer = new MediaPlayer();
    mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
    mediaPlayer.setOnCompletionListener(callbackDelegate);
    mediaPlayer.setOnErrorListener(callbackDelegate);
    mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
        public void onPrepared(MediaPlayer mp) {
            mediaPlayer.start();
        }
    });
}