为什么在停止 MediaRecorder 时会出现 IllegalStateException?
Why am I getting an IllegalStateException when stopping MediaRecorder?
为了更好地理解 Android 中的音频录制和播放,我在 android 开发教程 https://developer.android.com/guide/topics/media/mediarecorder#sample-code
中实现了代码的克隆
开始录制时没有出现任何错误,但是,我还没有验证是否真的在进行任何录制。然后当我停止录制时,应用程序崩溃到之前的 activity 并且在随后的尝试中弹出 "app keeps crashing" 对话框并且应用程序退出。
我在 AndroidManifest
中有 <uses-permission android:name="android.permission.RECORD_AUDIO" />
private void onRecord(boolean start) {
if (start) {
startRecording();
} else {
stopRecording(); // Line 53 //
}
}
private void startRecording() {
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setOutputFile(fileName);
System.out.println(fileName);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
try {
recorder.prepare();
} catch (IOException e) {
Log.e(LOG_TAG, "prepare() failed");
//e.printStackTrace();
}
}
private void stopRecording() {
recorder.stop(); // Line 98 //
recorder.release();
recorder = null;
}
class RecordButton extends AppCompatButton {
boolean mStartRecording = true;
OnClickListener clicker = new OnClickListener() {
public void onClick(View v) {
onRecord(mStartRecording);
if(mStartRecording) {
setText(R.string.stop_recording);
} else {
setText(R.string.start_recording);
}
mStartRecording = !mStartRecording;
}
};
public RecordButton(Context ctx){
super(ctx);
setText(R.string.start_recording);
setOnClickListener(clicker);
}
}
Logcat 输出:
2020-02-14 10:57:09.789 23619-23619/? E/Zygote: accessInfo : 1
2020-02-14 10:57:09.820 23619-23619/? E/.xxxxxx.xxxxxx: Unknown bits set in runtime_flags: 0x8000
2020-02-14 10:57:12.925 23619-23619/xx.xxxxxxx.xxxxxx.xxxxxxx E/MediaRecorder: stop called in an invalid state: 8
2020-02-14 10:57:12.927 23619-23619/xx.xxxxxxx.xxxxxx.xxxxxxx E/AndroidRuntime: FATAL EXCEPTION: main
Process: xx.xxxxxxx.xxxxxx.xxxxxxx, PID: 23619
java.lang.IllegalStateException
at android.media.MediaRecorder._stop(Native Method)
at android.media.MediaRecorder.stop(MediaRecorder.java:1440)
at xx.xxxxxxx.xxxxxx.xxxxxxx.RecordActivity.stopRecording(RecordActivity.java:98)
at xx.xxxxxxx.xxxxxx.xxxxxxx.RecordActivity.onRecord(RecordActivity.java:53)
at xx.xxxxxxx.xxxxxx.xxxxxxx.RecordActivity.access[=11=]0(RecordActivity.java:21)
at xx.xxxxxxx.xxxxxx.xxxxxxx.RecordActivity$RecordButton.onClick(RecordActivity.java:108)
at android.view.View.performClick(View.java:7866)
at android.widget.TextView.performClick(TextView.java:14952)
at android.view.View.performClickInternal(View.java:7839)
at android.view.View.access00(View.java:886)
at android.view.View$PerformClick.run(View.java:29363)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:237)
at android.app.ActivityThread.main(ActivityThread.java:7811)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1076)
如果需要,我可以提供任何相关的附加代码或详细信息
编辑
请求权限如下:
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case REQUEST_RECORD_AUDIO_PERMISSION:
permissionToRecordAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
break;
}
if (!permissionToRecordAccepted ) finish();
}
并在 onCreate() 内部:
ActivityCompat.requestPermissions(this, permissions, REQUEST_RECORD_AUDIO_PERMISSION);
编辑 2 *
我已验证正在创建录音文件,但大小为 0 B
媒体播放器
在下面的 flowchart 中,stop()
有效状态是 {Prepared, Started, Stopped, Paused, PlaybackCompleted}
。其他状态(空闲、初始化、错误)是 不 有效状态,无法调用 stop()
。否则,IllegalStateException
抛出。
因此,对于 Ensure,我们应该为 MediaPlayer 设置状态 listeners。因为 Prepared
状态比 setOnPreparedListener
更早初始化 player
我们可以安全地停止播放器。
...
boolean isPrepared = false;
...
recorder.setOnPreparedListener ....
{
...
isPrepared = true;
...
}
...
private void stopRecording() {
if(isPrepared){
player.stop();
...
}
}
为了更好理解,我做了代码总结。
媒体记录器
下面flowchart中,stop()
只有在录音状态下才可用。否则,调用 stop()
会抛出 IllegalStateException
.
为了安全地停止或释放记录器资源,我们必须确保记录器已经启动。
recorder.start(); // Recording is now started
针对您的特定代码
boolean isRecording = false;
private void startRecording() {
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setOutputFile(fileName);
System.out.println(fileName);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
try {
recorder.prepare();
recorder.start();
isRecording = true;
} catch (IOException e) {
Log.e(LOG_TAG, "prepare() failed");
//e.printStackTrace();
}
}
....
private void stopRecording() {
if(isRecording){
recorder.stop();
}
recorder.reset(); // You can reuse the object by going back to setAudioSource() step
recorder.release(); // Now the object cannot be reused
isRecording = false;
}
如果我们处于录音状态,停止然后释放它,否则就释放它。
为了更好地理解 Android 中的音频录制和播放,我在 android 开发教程 https://developer.android.com/guide/topics/media/mediarecorder#sample-code
中实现了代码的克隆开始录制时没有出现任何错误,但是,我还没有验证是否真的在进行任何录制。然后当我停止录制时,应用程序崩溃到之前的 activity 并且在随后的尝试中弹出 "app keeps crashing" 对话框并且应用程序退出。
我在 AndroidManifest
中有<uses-permission android:name="android.permission.RECORD_AUDIO" />
private void onRecord(boolean start) {
if (start) {
startRecording();
} else {
stopRecording(); // Line 53 //
}
}
private void startRecording() {
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setOutputFile(fileName);
System.out.println(fileName);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
try {
recorder.prepare();
} catch (IOException e) {
Log.e(LOG_TAG, "prepare() failed");
//e.printStackTrace();
}
}
private void stopRecording() {
recorder.stop(); // Line 98 //
recorder.release();
recorder = null;
}
class RecordButton extends AppCompatButton {
boolean mStartRecording = true;
OnClickListener clicker = new OnClickListener() {
public void onClick(View v) {
onRecord(mStartRecording);
if(mStartRecording) {
setText(R.string.stop_recording);
} else {
setText(R.string.start_recording);
}
mStartRecording = !mStartRecording;
}
};
public RecordButton(Context ctx){
super(ctx);
setText(R.string.start_recording);
setOnClickListener(clicker);
}
}
Logcat 输出:
2020-02-14 10:57:09.789 23619-23619/? E/Zygote: accessInfo : 1
2020-02-14 10:57:09.820 23619-23619/? E/.xxxxxx.xxxxxx: Unknown bits set in runtime_flags: 0x8000
2020-02-14 10:57:12.925 23619-23619/xx.xxxxxxx.xxxxxx.xxxxxxx E/MediaRecorder: stop called in an invalid state: 8
2020-02-14 10:57:12.927 23619-23619/xx.xxxxxxx.xxxxxx.xxxxxxx E/AndroidRuntime: FATAL EXCEPTION: main
Process: xx.xxxxxxx.xxxxxx.xxxxxxx, PID: 23619
java.lang.IllegalStateException
at android.media.MediaRecorder._stop(Native Method)
at android.media.MediaRecorder.stop(MediaRecorder.java:1440)
at xx.xxxxxxx.xxxxxx.xxxxxxx.RecordActivity.stopRecording(RecordActivity.java:98)
at xx.xxxxxxx.xxxxxx.xxxxxxx.RecordActivity.onRecord(RecordActivity.java:53)
at xx.xxxxxxx.xxxxxx.xxxxxxx.RecordActivity.access[=11=]0(RecordActivity.java:21)
at xx.xxxxxxx.xxxxxx.xxxxxxx.RecordActivity$RecordButton.onClick(RecordActivity.java:108)
at android.view.View.performClick(View.java:7866)
at android.widget.TextView.performClick(TextView.java:14952)
at android.view.View.performClickInternal(View.java:7839)
at android.view.View.access00(View.java:886)
at android.view.View$PerformClick.run(View.java:29363)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:237)
at android.app.ActivityThread.main(ActivityThread.java:7811)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1076)
如果需要,我可以提供任何相关的附加代码或详细信息
编辑 请求权限如下:
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case REQUEST_RECORD_AUDIO_PERMISSION:
permissionToRecordAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
break;
}
if (!permissionToRecordAccepted ) finish();
}
并在 onCreate() 内部:
ActivityCompat.requestPermissions(this, permissions, REQUEST_RECORD_AUDIO_PERMISSION);
编辑 2 * 我已验证正在创建录音文件,但大小为 0 B
媒体播放器
在下面的 flowchart 中,stop()
有效状态是 {Prepared, Started, Stopped, Paused, PlaybackCompleted}
。其他状态(空闲、初始化、错误)是 不 有效状态,无法调用 stop()
。否则,IllegalStateException
抛出。
因此,对于 Ensure,我们应该为 MediaPlayer 设置状态 listeners。因为 Prepared
状态比 setOnPreparedListener
更早初始化 player
我们可以安全地停止播放器。
...
boolean isPrepared = false;
...
recorder.setOnPreparedListener ....
{
...
isPrepared = true;
...
}
...
private void stopRecording() {
if(isPrepared){
player.stop();
...
}
}
为了更好理解,我做了代码总结。
媒体记录器
下面flowchart中,stop()
只有在录音状态下才可用。否则,调用 stop()
会抛出 IllegalStateException
.
为了安全地停止或释放记录器资源,我们必须确保记录器已经启动。
recorder.start(); // Recording is now started
针对您的特定代码
boolean isRecording = false;
private void startRecording() {
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setOutputFile(fileName);
System.out.println(fileName);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
try {
recorder.prepare();
recorder.start();
isRecording = true;
} catch (IOException e) {
Log.e(LOG_TAG, "prepare() failed");
//e.printStackTrace();
}
}
....
private void stopRecording() {
if(isRecording){
recorder.stop();
}
recorder.reset(); // You can reuse the object by going back to setAudioSource() step
recorder.release(); // Now the object cannot be reused
isRecording = false;
}
如果我们处于录音状态,停止然后释放它,否则就释放它。