Search code examples
androidandroid-mediaplayerillegalstateexceptionringtone

java.lang.IllegalStateException in MediaPlayer.setDataSource, using Ringtone class


I had a crash reported by an user (Samsung Galaxy S5, Android 4.4) and I do not understand what is happening. It seems imporbable but maybe some people have encountered the same problem, or similar.

Here is the trace:

java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:300)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:841)
Caused by: java.lang.IllegalStateException
at android.media.MediaPlayer._setDataSource(Native Method)
at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1383)
at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1367)
at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1302)
at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1240)
at android.media.MediaPlayer.setDataSource(MediaPlayer.java:986)
at android.media.MediaPlayer.setDataSource(MediaPlayer.java:951)
at android.media.Ringtone.setUri(Ringtone.java:219)
at android.media.Ringtone.setStreamType(Ringtone.java:89)
at com.aasfet.clocklight.WakeActivity$RingAsyncTask.doInBackground(WakeActivity.java:510)
at com.aasfet.clocklight.WakeActivity$RingAsyncTask.doInBackground(WakeActivity.java:1)
at android.os.AsyncTask$2.call(AsyncTask.java:288)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
... 3 more

Here is the part of my code that is in the error:

private class RingAsyncTask extends AsyncTask<Integer, Integer, Integer> {

        @Override
        protected Integer doInBackground(Integer... params) {
            int previousVolume = audioManager.getStreamVolume(AudioManager.STREAM_ALARM);
            int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM);
            int newVolume = (int)(volume * (float)maxVolume);
            if(newVolume < 1){
                newVolume = 1;
            }
            if(progressive){
                audioManager.setStreamVolume(AudioManager.STREAM_ALARM, 1, 0);
            }else{
                audioManager.setStreamVolume(AudioManager.STREAM_ALARM, newVolume, 0);
            }
            getRingtone().setStreamType(AudioManager.STREAM_ALARM);
            getRingtone().play();
...

getRingtone() is one of my functions and returns a Ringtone object that was obtained with a RingtoneManager cursor in the activity's onResume, I do not think there is any problem there.

In the Android source, Ringtone.setStreamType calls Ringtone.setURI. Here is setURI:

 public void setUri(Uri uri) {
168        destroyLocalPlayer();
169
170        mUri = uri;
171        if (mUri == null) {
172            return;
173        }
174
175        // TODO: detect READ_EXTERNAL and specific content provider case, instead of relying on throwing
176
177        // try opening uri locally before delegating to remote player
178        mLocalPlayer = new MediaPlayer();
179        try {
180            mLocalPlayer.setDataSource(mContext, mUri);
181            mLocalPlayer.setAudioStreamType(mStreamType);
182            mLocalPlayer.prepare();
...

So, setURI creates a new MediaPlayer, then calls setDataSource on the MediaPlayer. The android doc tells us that using new MediaPlayer() sets it in "idle" state, and that "idle" state is the correct state for calling setDataSource. I really do no understand how, apparently one time in a thousand, I get this error. I cannot contact the user who had the error, never reproduced it on my end, so I find myself blocked. I'm thinking of catching the error in my code and when it happens, try again with my Ringtone.setStreamType, on the assumption that it happens because of a transient "state" of the system that may have changed a few milliseconds later. In short I am quite desperate :)

Any help or similar experiences would be appreciated :)


Solution

  • In my case, i had to call reset() on the MediaPlayer instance, before i supplied it with a different url.

    As far as the documentation goes, setDataSource is only valid in IDLE state.

    You can also visit this answer , explained very nicely using diagram.