Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java.lang.illegalStateException randomly occurs when getting mediaPlayer.getCurrentPosition

Tags:

java

android

This code works fine song play perfectly but some time randomly going to next song manually crashes application. Happens randomly

    updateSeekBar = new Thread() {
        @Override
        public void run() {
            int runtime = mediaPlayer.getDuration();
            int currentPosition = 0;
            while (currentPosition < runtime) {
                try {
                    sleep(500);
                    currentPosition = mediaPlayer.getCurrentPosition();//This is where the app crash                        
                    if (seekBar != null) {
                        seekBar.setProgress(currentPosition);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }
    };

Crash report

06-10 22:08:53.160 15351-15560/skydeveloper.me.musicx2 E/AndroidRuntime: FATAL EXCEPTION: Thread-6875
                                                                     Process: skydeveloper.me.musicx2, PID: 15351
                                                                     java.lang.IllegalStateException
                                                                         at android.media.MediaPlayer.getCurrentPosition(Native Method)
                                                                         at skydeveloper.me.musicx2.Player$1.run(Player.java:104)
like image 241
Skyyy Avatar asked Jun 03 '16 05:06

Skyyy


5 Answers

Actually this problem occurs if Media player is in wrong state.

If your MediaPlayer is in the Initialized-State, you cannot call start(). So you have to wait till your MediaPlayer is in the Prepared-State position. You can only seek the Mediaplayer in Prepared, Started and Paused State.

For more details you can check: Media Player State Diagram

You can also make second call for current position. As it is random issue then bypassing and again calling the method can save you.

mediaPlayer.reset();

try {
   .......
   .......
   currentPosition = mediaPlayer.getCurrentPosition();
   ......
} catch (IllegalStateException e) {
   mediaPlayer.reset();
   currentPosition = mediaPlayer.getCurrentPosition();
}

mediaPlayer.prepareAsync();

Resource Link: What is this error java.lang.IllegalStateException at MediaPlayer.getCurrentPosition



UPDATE1:

You can bypass the exception and make a second call by using it. Hope it may help you.

try {
....
currentPosition = mediaPlayer.getCurrentPosition();
....
} catch (final Exception e) {
    e.printStackTrace();
    if (e instanceof IllegalStateException) { // bypass IllegalStateException
        .......
        // You can again call the method and make a counter for deadlock situation or implement your own code according to your situation
        if (retry) {
               mediaPlayer.reset();
               currentPosition = mediaPlayer.getCurrentPosition();
        } else {
            throw e;
        }
    }
}

UPDATE2:

This will try 3 times to get current position. If not found, then it will give exception.

 try {
     ....
     currentPosition = mediaPlayer.getCurrentPosition();
     ....
     } catch (final Exception e) {
         e.printStackTrace();
         if (e instanceof IllegalStateException) { // bypass IllegalStateException
             .......
             // You can again call the method and make a counter for deadlock situation or implement your own code according to your situation
             boolean checkAgain = true;
             int counter = 0;
                 for(int i = 0; i < 2; i++){
                     if (checkAgain) {
                         mediaPlayer.reset();
                         currentPosition = mediaPlayer.getCurrentPosition();
                         if(currentPosition > 0) {
                             checkAgain = false;
                             counter++;
                         }
                     } else {
                         if(counter == 0){
                             throw e;
                         }
                     }
                 }


         }
     }

UPDATE4:

  1. An IllegalStateException is thrown if prepare() or prepareAsync() is called in any other state.
  2. While in the Prepared state, properties such as audio/sound volume, screenOnWhilePlaying, looping can be adjusted by invoking the corresponding set methods.

  3. To start the playback, start() must be called. After start() returns successfully, the MediaPlayer object is in the Started state. isPlaying() can be called to test whether the MediaPlayer object is in the Started state.

  4. While in the Started state, the internal player engine calls a user supplied OnBufferingUpdateListener.onBufferingUpdate() callback method if a OnBufferingUpdateListener has been registered beforehand via setOnBufferingUpdateListener(OnBufferingUpdateListener). This callback allows applications to keep track of the buffering status while streaming audio/video. Calling start() has not effect on a MediaPlayer object that is already in the Started state.

So isPlaying() method should be checked. So code will be look like below:

 try {
     ....
     currentPosition = mediaPlayer.getCurrentPosition();
     ....
     } catch (final Exception e) {
         e.printStackTrace();
         if (e instanceof IllegalStateException) { // bypass IllegalStateException
             .......
             // You can again call the method and make a counter for deadlock situation or implement your own code according to your situation
             boolean checkAgain = true;
             int counter = 0;
                 for(int i = 0; i < 2; i++){
                     if (checkAgain) {
                         mediaPlayer.reset();
                         if(mediaPlayer != null & mediaPlayer.isPLaying()) {
                            currentPosition = mediaPlayer.getCurrentPosition();
                         } else {
                            currentPosition = 0; 
                         }
                         if(currentPosition > 0) {
                             checkAgain = false;
                             counter++;
                         }
                     } else {
                         if(counter == 0){
                             throw e;
                         }
                     }
                 }


         }
     }
like image 149
SkyWalker Avatar answered Oct 17 '22 12:10

SkyWalker


In this code checks to see if the current position is before the end, and if so it waits 500ms, by which time it might be past the end! In fact for all practical purposes, it might reach the end just a moment after the check. This is not thread safe but there are synchronization solutions to mitigate this condition. I would suggest you use a simpler solution like this:

updateSeekBar = new Thread() {
        @Override
        public void run() {
            int runtime = mediaPlayer.getDuration();
            int currentPosition = 0;
            int adv = 0;
            while ((adv = ((adv = runtime - currentPosition) < 500)?adv:500) > 2) {
                try {
                    currentPosition = mediaPlayer.getCurrentPosition();
                    if (seekBar != null) {
                        seekBar.setProgress(currentPosition);
                    }
                    sleep(adv);    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (IllegalStateException e) {
                    seekBar.setProgress(runtime);
                    break;
                }

            }
        }
    };

This code first calculates the difference between the currentPosition and the duration and only if there is more than 2ms difference it will try to get the new current position (2ms is just an arbitrary number to add some safety margin while sacrificing 2ms of accuracy at the end.) Also sleep is moved to the end of the loop, which adds to the safety margin and also makes the display of the seekbar more inline with the current position. Lastly there is a catch statement added just in case everything else fails.

By the way, while it is not shown in your code, I am assuming seekBar is final so there is no need to check for null every time. Maybe move it to the top and exit if it is null.

like image 28
Kaamel Avatar answered Oct 17 '22 14:10

Kaamel


It may be possible, when you try to get the position of media player at that time player has stopped or released, so apply below conditions for that,

     if(mediaPlayer != null & mediaPlayer.isPLaying())
        currentPosition = mediaPlayer.getCurrentPosition();
     else 
        currentPosition = 0;

try below code,

     handler.postDelayed(new Runnable(){
        @Override
        public void run() {
            int runtime = mediaPlayer.getDuration();
            int currentPosition = 0;
            while (currentPosition < runtime) {
               try {
                    if(mediaPlayer != null & mediaPlayer.isPLaying())
                        currentPosition = mediaPlayer.getCurrentPosition();
                    else 
                       currentPosition = 0;    

                    if (seekBar != null) {
                          seekBar.setProgress(currentPosition);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
              }
          }
      }, 500);
like image 44
Vickyexpert Avatar answered Oct 17 '22 13:10

Vickyexpert


I believe the reason for this is the thread.sleep() line. it causes the current thread to sleep while the media player can be actually in a stopped or released state.

The docs mention that calling getCurrentPosition() must happen at one of the valid states:

{Idle, Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted}

Before calling getCurrentPosition, you can check that the media player is actually playing by checking isPlaying() value.

Also you can run your thread using a Timer and a TimerTask instead of using while loops and Thread.sleep();

like image 1
Mina Wissa Avatar answered Oct 17 '22 12:10

Mina Wissa


I strongly suspect that these methods are not thread safe. Even with the state checking that people are suggesting, you may still run into a problem if you interleave another operation from a different thread that renders this check useless.

Consider synchronizing on the media player object to prevent concurrent access. You'll have to declare the mediaPlayer to be final.

final MediaPlayer mediaPlayer = ...

And later (with one of the suggested checks. I didn't look to see if this was the appropriate one):

synchronized (mediaPlayer) {
  if (mediaPlayer != null & mediaPlayer.isPlaying()) {
    currentPosition = mediaPlayer.getCurrentPosition();
  } else {
    currentPosition = 0;
  }
}
like image 1
Zhe Avatar answered Oct 17 '22 12:10

Zhe