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() 我沒有測試,誰可以看看,
整個問題就這樣解決了