获取 mediaPlayer.getCurrentPosition 时,出现 java.lang.illegalStateException 异常

10
这段代码可以正常工作,歌曲播放得很好,但有时手动切换下一首歌会导致应用程序崩溃。这种情况是随机发生的。
    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();
                }

            }
        }
    };

故障报告

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)

1
http://stackoverflow.com/questions/16405594/what-is-this-error-java-lang-illegalstateexception-at-mediaplayer-getcurrentposi - MrPk
请发布完整的堆栈跟踪。 - petey
@petey 更新问题 - Skyyy
@Akash,你能分享一下你的媒体播放器初始化代码吗? - ikhsanudinhakim
这是因为您尝试在媒体播放器停止后进行播放,似乎在初始化时出现了错误。因此,请分享您完整的媒体播放器初始化代码,以便我们找出问题所在。 - ikhsanudinhakim
显示剩余2条评论
8个回答

6
实际上,如果媒体播放器处于错误状态,则会出现此问题。 如果您的MediaPlayer处于“Initialized-State”状态,则无法调用“start()”。因此,您必须等待直到您的MediaPlayer处于“Prepared-State”位置。 只有在准备、开始和暂停状态下才能搜索Mediaplayer。 更多详细信息请查看:Media Player State Diagram 您还可以进行第二次调用以获取当前位置。由于这是随机问题,因此规避并再次调用该方法可节省您的时间。
mediaPlayer.reset();

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

mediaPlayer.prepareAsync();

资源链接: 这是什么错误java.lang.IllegalStateException at MediaPlayer.getCurrentPosition



更新1:

您可以通过使用它来绕过异常并进行第二次调用。希望它能帮到您。

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

更新2:

此代码将尝试3次获取当前位置。如果未找到,则会抛出异常。

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


         }
     }

更新4:

  1. 如果在任何其他状态下调用prepare()prepareAsync(),将抛出IllegalStateException异常。
  2. 在准备就绪状态下,可以通过调用相应的设置方法来调整音频/声音音量、screenOnWhilePlaying、循环等属性。

  3. 要开始播放,必须调用start()。在start()成功返回后,MediaPlayer对象处于已启动状态。可以调用isPlaying()方法来测试MediaPlayer对象是否处于已启动状态。

  4. 在已启动状态下,如果已经通过setOnBufferingUpdateListener(OnBufferingUpdateListener)注册了OnBufferingUpdateListener,则内部播放器引擎会调用用户提供的OnBufferingUpdateListener.onBufferingUpdate()回调方法。这个回调允许应用程序在流式传输音频/视频时跟踪缓冲状态。对于已经处于已启动状态的MediaPlayer对象,调用start()没有任何影响。

因此,应检查isPlaying()方法。代码将如下所示:

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


         }
     }

我在调用start()方法后调用了getCurrentPosition()方法。我尝试了你上面的例子,但在mediaPlayer.reset();处的catch块中仍然出现了相同的异常。 - Skyyy
@AkashKumar 我已经更新了答案。请您检查并告诉我是否有任何更新? - SkyWalker
不明白 if(retry) 部分,retry 是什么? - Skyyy
重试意味着您可以再次调用该方法并为死锁情况设置计数器,或根据您的情况实现自己的代码。@AkashKumar - SkyWalker
@AkashKumar 你能否检查一下UPDATE2部分?我刚刚更新了。 - SkyWalker
@AkashKumar 应该检查 isPlaying() 方法。因此,请检查更新的部分4。 - SkyWalker

3

这段代码检查当前位置是否在结尾之前,如果是,则等待500ms,到那时,它可能已经过了结尾!实际上,对于所有实际的目的而言,在检查之后的一瞬间,它可能就到达了结尾。这不是线程安全的,但有同步解决方案可缓解这种情况。我建议您使用一个更简单的解决方案:

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

            }
        }
    };

该代码首先计算当前位置(currentPosition)和总时长(duration)之间的差异,只有在超过2ms的差异时,它才会尝试获取新的当前位置(2ms只是一个任意数字,用于增加一些安全边界,同时牺牲2ms的精度)。另外,将sleep移动到循环的末尾,这会增加安全边界,并使seekbar的显示更符合当前位置。最后,添加了一个catch语句以防万一其他所有操作都失败。
顺便说一下,虽然代码中没有显示,但我假设seekBar已设置为final,因此无需每次检查是否为空。也许将其移到顶部并在它为null时退出会更好。

2

当您尝试获取媒体播放器的位置时,可能会出现播放器停止或释放的情况,因此请应用以下条件:

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

请尝试以下代码:

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

现在它在 mediaPlayer.isPlaying() 的 if 条件中崩溃,出现相同的错误。 - Skyyy
好的,那么确认您是在非UI线程中调用此方法,因此导致应用程序崩溃。请尝试在UI线程或处理程序中编写代码,这样它将正常工作。 - Vickyexpert
我需要启动处理程序吗?这段代码没有更新SeekBar。 - Skyyy
实际上,通过 handler.postDelayed() 方法应该启动并执行操作,但如果它不起作用,则首先检查 currentPosition 的值,因为如果它为 0,则也不会更新 seekbar,如果不是,则需要启动它。 - Vickyexpert
实际上,一旦handler.postDelayed()开始运行,我就无法与应用程序进行交互。尝试与应用程序交互时会显示“应用程序未响应”。 - Skyyy

1
我认为原因是thread.sleep()代码行。它会使当前线程休眠,而媒体播放器实际上可能处于停止或释放状态。
文档提到调用getCurrentPosition()必须在一个有效状态之一:
{空闲,已初始化,已准备好,已启动,已暂停,已停止,播放完成}
在调用getCurrentPosition之前,您可以通过检查isPlaying()的值来确保媒体播放器正在播放。
此外,您可以使用TimerTimerTask来运行您的线程,而不是使用while循环和Thread.sleep()。

如果检查 mediaPlayer.isPlaying,那么在 mediaPlayer.isPlaying 处应用程序会崩溃。我将检查 Timer 和 TimerTask。 - Skyyy

1
我强烈怀疑这些方法不是线程安全的。即使使用人们建议的状态检查,如果您交错了来自另一个线程的其他操作,可能仍然会遇到问题,从而使此检查无效。
考虑在媒体播放器对象上同步以防止并发访问。您将不得不声明 mediaPlayer 为 final。
final MediaPlayer mediaPlayer = ...

后来(使用建议之一的检查方法。我没有查看是否是适当的方法):

synchronized (mediaPlayer) {
  if (mediaPlayer != null & mediaPlayer.isPlaying()) {
    currentPosition = mediaPlayer.getCurrentPosition();
  } else {
    currentPosition = 0;
  }
}

0
请遵循状态图:输入图像描述 您会意识到,您应该调用reset()将其恢复到空闲状态。只有这样,您才能调用setDataSource()。

0
尝试这样做: 将mMediaPlayer.start();替换为以下内容:
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mediaPlayer) {
                    mMediaPlayer.start();
                }
});

0
通过创建基于状态图的自定义mediaPlayer状态,我能够调用getCurrentPosition()方法而不会出现任何异常。 对于我的音频播放器,我创建了以下状态。
private enum class MediaPlayerState {
    IDLE,
    PLAYING,
    PAUSED,
    COMPLETED,
    PREPARED
}

然后创建监听器来更改我的音频播放器的状态

private val prepareListener = MediaPlayer.OnPreparedListener {
    Log.d("MyLog", "Media State = PREPARED")
    mediaState = MediaPlayerState.PREPARED
    
   }
private val onCompletedListener = MediaPlayer.OnCompletionListener {
    Log.d("MyLog", "Media State = Completed")
    mediaState = MediaPlayerState.COMPLETED
   }

所以,只有在媒体状态变量处于准备就绪状态时,我才能播放媒体

when(mediaState)
 {
   MediaPlayerState.COMPLETED, MediaPlayerState.PREPARED -> {

      mediaPlayer.start()
      mediaState = MediaPlayerState.PLAYING
     }
     ... 
     }
              

所以当我释放媒体播放器时,我将状态更改为空闲

 override fun onDetachedFromWindow() {
    super.onDetachedFromWindow()
    mediaState = MediaPlayerState.IDLE
    mediaPlayer.release()
    Log.d("MyLog", "Media State = Released")

}

然后当我在媒体上调用 getCurrentPosition 时,我会检查状态(在后台线程上)

 if(mediaState == MediaPlayerState.PLAYING)
                seekBar.progress = mediaPlayer.currentPosition

因此,没有非法状态异常

通过以组合的方式思考,改变了做事的方式...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接