1. 程式人生 > >android中java.lang.IllegalStateException異常產生的原因及解決辦法

android中java.lang.IllegalStateException異常產生的原因及解決辦法

11-09 13:33:56.080: E/MediaRecorder(19865): stop called in an invalid state: 0
11-09 13:33:56.080: I/MediaRecorder(19865): stop
11-09 13:33:56.080: E/MediaRecorder(19865): stop called in an invalid state: 1
11-09 13:33:56.080: E/InputEventReceiver(19865): Exception dispatching input event.
11-09 13:33:56.080: E/MessageQueue-JNI(19865): Exception in MessageQueue callback: handleReceiveCallback
11-09 13:33:56.080: E/MessageQueue-JNI(19865): java.lang.IllegalStateException
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.media.MediaRecorder.native_stop(Native Method)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.media.MediaRecorder.stop(MediaRecorder.java:1127)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at com.jarvis.message.SoundMeter.stop(SoundMeter.java:64)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at com.jarvis.message.SoundMeter.start(SoundMeter.java:38)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at com.jarvis.user.RusumeReadView.start(RusumeReadView.java:522)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at com.jarvis.user.RusumeReadView.access$26(RusumeReadView.java:520)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at com.jarvis.user.RusumeReadView$17.onTouch(RusumeReadView.java:469)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.View.dispatchTouchEvent(View.java:8287)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2324)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2011)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2324)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2011)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2324)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2011)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2324)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2011)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2324)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2011)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2324)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2011)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2324)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2011)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2324)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2011)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2324)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2011)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2324)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2011)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2324)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2011)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2324)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2011)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2324)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2011)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2345)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1708)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.app.Activity.dispatchTouchEvent(Activity.java:2797)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2306)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.View.dispatchPointerEvent(View.java:8483)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4291)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4157)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3662)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3715)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3681)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3791)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3689)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3848)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3662)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3715)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3681)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3689)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3662)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:5978)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:5952)
11-09 13:33:56.080: E/MessageQueue-JNI(19865): 	at android.view.ViewRootImpl.enqueueInputEvent(ViewRootIm

出現這個問題真的很難受,搞了好久,其實不同的機型有不同的效果,我在魅族 手機 華碩手機  華為p8上都沒有出現這樣的問題

在小米2s 在華為榮耀7 上都出現了這樣的bug  

接下來是分析真正的問題原因所在 解決辦法很簡單,就是捕獲這個異常,因為是空的,再重新new下,然後接著釋放就可以了

IllegalStateException這個異常它是指“非法的狀態”。

android的MediaRecorder 和MediaPlayer API中用到了JNI,也就是我們的java程式碼是要呼叫native的C++方法的

MediaRecorder ,MediaPlayer是用c++實現的),

出現這個異常,就是因為我們java裡面的MediaRecorder ,MediaPlayer物件的狀態和native的物件狀態發生了不一致。

問題的本質就是這樣的,那什麼叫狀態不一致,我們得用程式碼中來解析下才能看的懂了:

使用的時候我們應該都知道,MediaRecorder 和MediaPlayer 用的方法有哪些了

if (mMediaPlayer.isPlaying()) {
				mMediaPlayer.stop();
			}
			mMediaPlayer.reset();
			mMediaPlayer.setDataSource(name);
			mMediaPlayer.prepare();
			mMediaPlayer.start();

		if (mRecorder == null) {
			mRecorder = new MediaRecorder();
			mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
			mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
			mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
			mRecorder.setOutputFile(UtilsForChat.getMusicFilePath(context, name));
			try {
				mRecorder.prepare();
				mRecorder.start();

				mEMA = 0.0;
			} catch (IllegalStateException e) {
				System.out.print(e.getMessage());
			} catch (IOException e) {
				System.out.print(e.getMessage());
			}

		}
mRecorder.stop();
mRecorder.release();

我相信大家都對這些方法都不陌生的,只是需要我們知道他們的順序,但是僅僅知道順序肯定是沒有用的,

因為你照樣會產生上面的錯誤  比如專業的錯誤:

切換錄影暫停過快導致stop failed.
java.lang.RuntimeException: stop failed.
at android.media.MediaRecorder.stop(Native Method)

原因如下:
在呼叫start()後馬上呼叫stop(),時由於沒有生成有效的音訊或是視訊資料。解決方法:讓執行緒睡眠一定的時間,在測試後發現1秒幾乎是最短時間。

這個錯誤我下篇文章再介紹我遇到的情況是什麼樣的,這裡還是回到本bug的點子上

出現標題的bug就是stop release isPlaying這些函式會出現問題,而出現的問題就是java物件狀態和NAtive物件狀態不一致導致無法停止,無法釋放,無法播放

再往裡面說,就是這樣的:當我們第一次申請了一個MediaRecorder 錄音物件,我們在java中釋放掉了,那麼誰能告訴我釋放的是誰的物件嗎,我當時也糾結了,不知道,後來查閱別人說的,釋放的是jni的物件,而java物件只是把棧記憶體裡面釋放了,其實這裡面我到現在還是沒有搞明白是不是這個意思,這樣就導致了本地物件的堆記憶體還是存在的,而jni物件裡面什麼都沒有了,能明白這個意思把,就導致的不一致

stop出現的原因就是:你想停止它(停止的物件是jni裡面的),可惜的是你根本沒有這個物件你怎麼停止呢  這不就是出現了物件不一致了嗎

isPlaying出現的原因也就是:你想判斷他是否正在播放,那麼如果你根本就不存在,我怎麼去判斷呢,

release,也是一樣,不過這個裡面保持總是在stop後面,所以出現的少

問題分析到這裡來了,我想大家應該都會解決了,只要找到這個異常,try下然後把java物件申請出來,不就可以使得jni和java物件一致了嗎,然後你再停止就ok拉

程式碼就是這樣的:

	public void stop() {
		if (mRecorder != null) {
			try {
				mRecorder.stop();
			} catch (IllegalStateException e) {
				// TODO 如果當前java狀態和jni裡面的狀態不一致,
				//e.printStackTrace();
				mRecorder = null;
				mRecorder = new MediaRecorder();
			}
			mRecorder.release();
			mRecorder = null;
		}
	}

對於在java中:mRecorder = new MediaRecorder(); 我相信這句程式碼大家應該都知道,java機制是怎麼樣處理的

java會分別在堆記憶體和棧記憶體中操作,一個是內容,一個是地址,內容引用地址或者叫指向地址值

這樣加上try之後,我發現錄音你不管怎麼操作都沒有問題了,後來我想了下,這個程式碼順序應該跟api中還是不一致

	public void stop() {
		if (mRecorder != null) {
			try {
				mRecorder.stop();
			} catch (IllegalStateException e) {
				// TODO 如果當前java狀態和jni裡面的狀態不一致,
				//e.printStackTrace();
				mRecorder = null;
				mRecorder = new MediaRecorder();
				mRecorder.stop();
			}
			mRecorder.release();
			mRecorder = null;
		}
	}

這個程式碼多了一行  stop()  我沒有測試,誰可以看看,

整個問題就這樣解決了