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?
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.
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().
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();
}
Two ways this can happen:
- 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.
- 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();
}
});
}