当前位置: 代码迷 >> Android >> Android使用MediaPlayer开发时投IllegalStateException
  详细解决方案

Android使用MediaPlayer开发时投IllegalStateException

热度:118   发布时间:2016-04-28 04:13:34.0
Android使用MediaPlayer开发时抛IllegalStateException
  在我开发的语音播放程序中,首次播放语音没问题,第二次播放时就抛出IllegalStateException异常,由于项目时间比较赶,大致查了下,基本明白问题的原因了,自己debug也证实了一些个推论,但最佳的解决方法却未能找到,只有一个自己想到的笨办法,和同样遇到这问题的人分享一下。
  首先要明确IllegalStateException这个异常是什么意思,它是指“非法的状态”。据我调查所知,android的mediaplayer API中用到了JNI,也就是我们的java代码是要调用native的C++方法的(mediaplayer是用c++实现的),而这里之所以出现这个异常,就是因为我们java里面的mediaplayer对象的状态和native的对象状态发生了不一致。这个问题再stackOverFlow上面有人问过,虽然回答的人没有给出具体的解决方案,但是原因说的很清楚了:http://stackoverflow.com/questions/15730772/android-java-lang-illegalstateexception-mediaplayer-isplaying/15730932,回答中也给出了mediaplayer的c++源码:http://androidxref.com/4.2.2_r1/xref/frameworks/base/media/jni/android_media_MediaPlayer.cpp#380,对于我来说,异常时发生在调用isPlaying()方法时,所以查看源码的isPlaying方法,有这么一句:
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);379    if (mp == NULL ) {380        jniThrowException(env, "java/lang/IllegalStateException", NULL);381        return false;382    }

  可见确实是native的mediaplayer对象为空引起的(但是我本地的java对象确实不为空,至今为查明原因),这里再把我的方法贴出来,根据里面的注释就能很清楚我的问题在哪里,以及解决方法:
private void doPlayVoice(String src, final VoiceViewHolder vh,			final boolean isLeft, int position) {		if (mp == null)		{			mp = new MediaPlayer();		}		// 为解决第二次播放时抛出的IllegalStateException,这里做了try-catch处理		boolean isPlaying = false;		try {			isPlaying = mp.isPlaying();		}		catch (IllegalStateException e) {			mp = null;			mp = new MediaPlayer();		}				if (isPlaying)		{			mp.stop();			mp.release();			mp = null;			nowPlayingPosition = -1;			mp = new MediaPlayer();			if (lastAnim != null && lastAnim.isRunning())			{				animStop(lastAnim, lastVH, lastIsLeft);			}		}		try		{			mp.setDataSource(src);			mp.setOnPreparedListener(new OnPreparedListener() {				@Override				public void onPrepared(MediaPlayer mp) {					mp.start();				}			});			// Prepare to async playing			mp.prepareAsync();		} catch (IllegalArgumentException e)		{			e.printStackTrace();		} catch (SecurityException e)		{			e.printStackTrace();		} catch (IllegalStateException e)		{			e.printStackTrace();		} catch (IOException e)		{			e.printStackTrace();		}		nowPlayingPosition = position;		final AnimationDrawable animationDrawable = animStart(vh, isLeft);		mp.setOnCompletionListener(new OnCompletionListener() {			@Override			public void onCompletion(MediaPlayer mp)			{				/* 在目前的代码结构下,mp.release()生效(即设nativeContext=0)了,mp = null却未生效,				 * 导致在下一次播放语音(即下次调用doPlayVoice)时,mp对象不为null,而nativeContext				 * 却已经被release掉了,于是在执行mp.isPlaying()时就发生了IllegalStateException,为什么				 * 会发生这样【mp.release()生效了,mp = null却未生效】的状况,原因暂未查明,为解决改异常				 * 在doPlayVoice方法的开头mp.isPlaying()处加上了try-catch语句,发生异常时即执行mp = null;				 * mp = new MediaPlayer()两句,以恢复mp的状态为正常,效果是一样的。				 */				mp.release();				mp = null;				animStop(animationDrawable, vh, isLeft);				nowPlayingPosition = -1;			}		});		lastAnim = animationDrawable;		lastVH = vh;		lastIsLeft = isLeft;	}

其实就像另外一个stackoverflow中有人说的:
“MediaPlayer can be strange though; it's worth playing around with different statements even if the logic already makes sense; I could help you more in this regard if you posted code.

For now, you could just use a try-catch statement and put something in the catch to ensure that MediaPlayer is working properly.”(http://stackoverflow.com/questions/12208696/media-player-isplaying-throws-illegal-state-android)。Mediaplayer的状态有时候是很奇怪的,即便我们的代码逻辑已经看着很完善了,还是应该做一些对于各种异常的捕获。
  还有一篇文章挺好,可以更清楚的了解一下android的mediaplayer的状态:
http://www.360doc.com/content/12/0703/16/7724936_222044896.shtml
  PS:有些人说是因为多个线程同时调用mediaplayer的关系 ,但我是在UI线程里做的,所以不涉及他们的说法,最终我的解决方法可能未必是最优的,如果有人有更好的方法,也请不吝赐教。
  相关解决方案