Android MediaRecorder 无法正常工作并在 stop() 方法上崩溃

问题描述

我正在学习 android 开发,我正在使用 MediaRecorder 类来录制音频。 logcat 说应用程序在 stop() 方法中崩溃并抛出 IllegalStateException。我不明白为什么它会崩溃,我在开始之前调用方法 stop。

public class MainActivity extends AppCompatActivity {

private MediaRecorder grabacion;
private String archivoSalida = null;
private Button btn_recorder;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    btn_recorder = (Button)findViewById(R.id.btn_rec);

    if (ContextCompat.checkSelfPermission(getApplicationContext(),Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getApplicationContext(),Manifest.permission.RECORD_AUdio) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.RECORD_AUdio,},1000);
    }
}

public void Recorder(View view){
    if(grabacion == null){
        archivoSalida = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Grabacion.mp3";
        grabacion = new MediaRecorder();
        grabacion.setAudioSource(MediaRecorder.AudioSource.MIC);
        grabacion.setoutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
        grabacion.setAudioEncoder(MediaRecorder.OutputFormat.AMR_NB);
        grabacion.setoutputFile(archivoSalida);

        try{
            grabacion.prepare();
            grabacion.start();
        } catch (IOException e){
        }

        btn_recorder.setBackgroundResource(R.drawable.rec);
        Toast.makeText(getApplicationContext(),"Grabando...",Toast.LENGTH_SHORT).show();
    } else if(grabacion != null){
        grabacion.stop();
        grabacion.release();
        grabacion = null;
        btn_recorder.setBackgroundResource(R.drawable.stop_rec);
        Toast.makeText(getApplicationContext(),"Grabación finalizada",Toast.LENGTH_SHORT).show();
    }
}

public void reproducir(View view) {

    MediaPlayer mediaPlayer = new MediaPlayer();
    try {
        mediaPlayer.setDataSource(archivoSalida);
        mediaPlayer.prepare();
    } catch (IOException e){
    }

    mediaPlayer.start();
    Toast.makeText(getApplicationContext(),"Reproduciendo audio",Toast.LENGTH_SHORT).show();
}}

这是清单文件

<?xml version="1.0" encoding="utf-8"?>

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUdio"/>

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>
什么 logcat 说:
E/MediaRecorder: stop called in an invalid state: 4
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.recorder,PID: 5386
    java.lang.IllegalStateException: Could not execute method for android:onClick
        at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:414)
        at android.view.View.performClick(View.java:7870)
        at android.widget.TextView.performClick(TextView.java:14970)
        at android.view.View.performClickInternal(View.java:7839)
        at android.view.View.access$3600(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:7948)
        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:1075)
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invoke(Native Method)
        at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:409)
        at android.view.View.performClick(View.java:7870) 
        at android.widget.TextView.performClick(TextView.java:14970) 
        at android.view.View.performClickInternal(View.java:7839) 
        at android.view.View.access$3600(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:7948) 
        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:1075) 
     Caused by: java.lang.IllegalStateException
        at android.media.MediaRecorder._stop(Native Method)
        at android.media.MediaRecorder.stop(MediaRecorder.java:1440)
        at com.example.recorder.MainActivity.Recorder(MainActivity.java:55)
        at java.lang.reflect.Method.invoke(Native Method) 
        at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:409) 
        at android.view.View.performClick(View.java:7870) 
        at android.widget.TextView.performClick(TextView.java:14970) 
        at android.view.View.performClickInternal(View.java:7839) 
        at android.view.View.access$3600(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:7948) 
        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:1075) 
I/Process: Sending signal. PID: 5386 SIG: 9

谢谢!

解决方法

stop() 方法的 documentation 声明它将抛出一个 IllegalStateException

如果它在 start() 之前被调用

由于这似乎是唯一一次抛出此错误的情况,因此您的情况很可能也是如此。

看看您的 Recorder() 方法。只有当您的变量不为空时才会调用 stop()。这意味着您的实例已正确初始化。 但是,由于抛出了异常,这意味着即使您的变量引用了 MediaRecorder 的实例,但它并未启动。

要找出原因,请查看应该启动您的 MediaRecorder 的代码:

    try {
        grabacion.prepare();
        grabacion.start();
    } catch (IOException e){
    }

虽然它被放置在 try-catch 结构中,但 catch 块实际上并不包含任何代码,导致任何错误都被简单地忽略。 因此,您遇到的问题是 grabacion.prepare();grabacion.start(); 未成功导致记录器无法启动。

鉴于 start() 仅根据其 documentation 抛出 IllegalStateException, 对 prepare() 的调用应该是问题的原因。这也符合我自己的经验,因为调用 prepare() 时可能会出错很多。以下是它根据 docs 中的描述执行的操作:

准备记录器以开始捕获和编码数据。此方法必须在设置所需的音视频源、编码器、文件格式等之后,但在 start() 之前调用。

所以,如您所知,现在为我查明确切原因并不容易。但是,我要补充的是,您使用了 Environment.getExternalStorageDirectory(),它是 API 29 中的 deprecated 来改善用户隐私。虽然这可能是您的问题所在,但我无法完全确定。

要准确找出导致错误的原因,您应该利用在 try-catch 中获得的 IOException。例如,您可以记录其堆栈跟踪以了解有关该问题的更多信息。