8

To get a better understanding of audio recording and playback in Android, I've implemented a clone of the code in the android dev tutorial https://developer.android.com/guide/topics/media/mediarecorder#sample-code

No errors occur upon starting the recording, however, I have yet to verify if any recording is actually taking place. Then when I stop recording, the app crashes to the previous activity and on a subsequent attempt the "app keeps crashing" dialog pops up and the app exits.

I have <uses-permission android:name="android.permission.RECORD_AUDIO" /> in the AndroidManifest

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 output:

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)

I can provide any relevant additional code or details if needed

EDIT Permission is requested with the following:

    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();

    }

and inside onCreate():

ActivityCompat.requestPermissions(this, permissions, REQUEST_RECORD_AUDIO_PERMISSION);

EDIT 2 * I've verified that the audio recording file is being created but is 0 B in size

7
  • First realase it, and then stop it. Commented Feb 14, 2020 at 18:38
  • It seems like usually we want to avoid recycling a running operation but I tried it anyway - still crashes on recorder.stop()
    – DataMeta
    Commented Feb 14, 2020 at 19:44
  • do you request permission? Commented Feb 14, 2020 at 20:16
  • Yes, added edit in post wit hte permissions code
    – DataMeta
    Commented Feb 14, 2020 at 20:57
  • please put the codes inside startRecording() Commented Feb 15, 2020 at 1:14

1 Answer 1

6

Media Player

in the below flowchart, stop() valid states are {Prepared, Started, Stopped, Paused, PlaybackCompleted}. other states(Idle,Initialized,Error) are NOT valid states in order to call stop(). otherwise, IllegalStateException thrown.

enter image description here

Therefore, for Ensure we should set states listeners for the MediaPlayer. Because the Prepared state is initialized earlier than by setOnPreparedListener for player we can safely stop the player.

...
boolean isPrepared = false;
...
recorder.setOnPreparedListener ....
{
...
isPrepared = true;
...
}
...
private void stopRecording() {
    if(isPrepared){
        player.stop();  
        ...
    }
}

I made the code summarized for better understanding.

Media Recorder

in the below flowchart, stop() is available only if you are in recording state. otherwise, calling stop() throws IllegalStateException.

enter image description here

in order to safely stop or release recorder resources, we have to make sure the recorder has been started.

recorder.start();   // Recording is now started 

for your specific code

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;
}

if we are in the recording state stop it then release it otherwise just release it.

4
  • Thanks for answering. I'm trying to understand what the purpose is of checking if the player is prepared, if the error involves incorrect stoppage of the recorder. Or rather why we use MediaPlayer.OnPreparedListener and not MediaRecorder.OnPreparedListener. In fact the latter doesn't seem to exist
    – DataMeta
    Commented Feb 15, 2020 at 2:18
  • @DataMeta you have to set listener depend on your settings. recorder or player.if you are using recorder use MeidaRecorder otherwise use MediaPlayer Commented Feb 15, 2020 at 2:37
  • What I'm trying to say is, it looks like there is no OnPreparedListener for MediaRecorder?
    – DataMeta
    Commented Feb 15, 2020 at 2:51
  • @DataMeta my pleasure . Commented Feb 15, 2020 at 3:28

Not the answer you're looking for? Browse other questions tagged or ask your own question.