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
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.
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.
in the below flowchart, stop()
is available only if you are in recording state. otherwise, calling stop()
throws IllegalStateException
.
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.