为啥我在停止 MediaRecorder 时会收到 IllegalStateException?

Posted

技术标签:

【中文标题】为啥我在停止 MediaRecorder 时会收到 IllegalStateException?【英文标题】:Why am I getting an IllegalStateException when stopping MediaRecorder?为什么我在停止 MediaRecorder 时会收到 IllegalStateException? 【发布时间】:2020-02-14 16:12:33 【问题描述】:

为了更好地理解 android 中的音频录制和播放,我在 android 开发教程 https://developer.android.com/guide/topics/media/mediarecorder#sample-code 中实现了代码的克隆

开始录制时没有出现任何错误,但是,我还没有验证是否真的进行了任何录制。然后当我停止录制时,应用程序崩溃到上一个活动,并且在随后的尝试中“应用程序不断崩溃”对话框弹出并且应用程序退出。

我在 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$000(RecordActivity.java:21)
        at xx.xxxxxxx.xxxxxx.xxxxxxx.RecordActivity$RecordButton$1.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.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: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

【问题讨论】:

先重新编译,再停止。 似乎我们通常希望避免回收正在运行的操作,但我还是尝试了 - 仍然在 recorder.stop() 上崩溃 您请求许可吗? 是的,在帖子中添加了带有权限代码的编辑 请将代码放入 startRecording() 【参考方案1】:

媒体播放器

在下面的flowchart、stop() 中,有效状态为Prepared, Started, Stopped, Paused, PlaybackCompleted。其他状态(空闲、初始化、错误)是 NOT 有效状态,以便调用 stop()。否则,IllegalStateException 被抛出。

因此,对于 Ensure 我们应该为MediaPlayer 设置状态listeners。因为Prepared 状态的初始化早于setOnPreparedListener for 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;

如果我们处于录制状态,请停止它,然后释放它,否则就释放它。

【讨论】:

感谢您的回答。我试图了解检查播放器是否准备好的目的是什么,如果错误涉及记录器的错误停止。或者更确切地说,为什么我们使用MediaPlayer.OnPreparedListener 而不是MediaRecorder.OnPreparedListener。事实上后者似乎并不存在 @DataMeta 您必须设置侦听器取决于您的设置。录音机或播放器。如果您使用的是录音机,请使用 MeidaRecorder,否则请使用 MediaPlayer 我想说的是,MediaRecorder 好像没有 OnPreparedListener? @DataMeta 我的荣幸。

以上是关于为啥我在停止 MediaRecorder 时会收到 IllegalStateException?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我在使用 foreach 时会收到 ***Error?

为啥我在使用 img 标签时会收到 403 禁止?

为啥我在尝试使用 HttpListener 时会收到“AccessDenied”? [复制]

为啥我在函数内部调用时会收到“错误:无效的挂钩调用”?

为啥我在实际工作时会收到“Fetch failed loading”?

为啥我在使用 RemoteFileTemplate 时会在日志中收到意外的 RuntimeException 警告?