Android开发:MediaRecorder.stop()调用致应用崩溃求助
Hey there, let's break down your crash issue and fix it step by step. First, let's highlight the critical errors from your Logcat that point to the root problems:
E/MediaRecorder: start failed: -38
E/MediaRecorder: stop called in an invalid state: 0
java.lang.IllegalStateException at android.media.MediaRecorder.native_stop(Native Method)
Root Causes
- Mismatched Output Format & File Extension
You initialized the recorder withOutputFormat.MPEG_4but switched toTHREE_GPPduring re-recording, while your output file uses a.3gpextension (which only aligns withTHREE_GPP). This conflict triggers thestart failed: -38error, meaning the recorder never actually starts recording. - Unhandled Start Failure State
Whenstart()fails, you catch the exception but don't reset the button states. This leaves thestopbutton enabled even though no recording is active—clicking it triggers an invalid state crash. - Improper MediaRecorder Resource Management
SettingmyAudioRecorder = nullafter stopping skips proper resource release, and your re-recording logic doesn't account for failed initial attempts. - SharedPreferences Logic Bug
Your path value calculation runs before committing changes, leading to incorrect file naming on the first app launch.
Fixed Code
Here's the revised window2 class with all issues addressed:
public class window2 extends AppCompatActivity implements AdapterView.OnItemSelectedListener{ Spinner spinner; Button play, stop, record; String outputFile; MediaRecorder myAudioRecorder; int reRecord = 0; int pathVal = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_window2); spinner = findViewById(R.id.spinner); spinner.setOnItemSelectedListener(this); // Fix SharedPreferences logic: get current value first, modify, commit, then use SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); pathVal = sharedPreferences.getInt("pathVal", 0); SharedPreferences.Editor edit = sharedPreferences.edit(); pathVal = (pathVal == 0) ? 1 : pathVal + 1; edit.putInt("pathVal", pathVal); edit.apply(); // Use apply() for async, safer than synchronous commit() play = findViewById(R.id.button14); stop = findViewById(R.id.button13); record = findViewById(R.id.button11); stop.setEnabled(false); play.setEnabled(false); // Use consistent THREE_GPP format matching .3gp extension outputFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + pathVal + ".3gp"; initMediaRecorder(); record.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { play.setEnabled(false); try { if (reRecord == 1) { // Re-initialize recorder with consistent settings for re-recording initMediaRecorder(); } myAudioRecorder.prepare(); myAudioRecorder.start(); record.setEnabled(false); stop.setEnabled(true); Toast.makeText(window2.this, "Recording...", Toast.LENGTH_SHORT).show(); } catch (IllegalStateException ise) { ise.printStackTrace(); resetRecordingState(); Toast.makeText(window2.this, "Recording failed: Invalid state", Toast.LENGTH_SHORT).show(); } catch (IOException ioe) { ioe.printStackTrace(); resetRecordingState(); Toast.makeText(window2.this, "Recording failed: IO error", Toast.LENGTH_SHORT).show(); } } }); stop.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { try { myAudioRecorder.stop(); myAudioRecorder.release(); // Properly release recorder resources reRecord = 1; record.setEnabled(true); stop.setEnabled(false); play.setEnabled(true); Toast.makeText(window2.this, "Recorded!", Toast.LENGTH_SHORT).show(); } catch (IllegalStateException e) { e.printStackTrace(); resetRecordingState(); Toast.makeText(window2.this, "No active recording to stop", Toast.LENGTH_SHORT).show(); } } }); play.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { MediaPlayer mediaPlayer = new MediaPlayer(); try { mediaPlayer.setDataSource(outputFile); mediaPlayer.prepare(); mediaPlayer.start(); Toast.makeText(window2.this, "Playing.", Toast.LENGTH_SHORT).show(); // Release MediaPlayer once playback finishes to avoid leaks mediaPlayer.setOnCompletionListener(mp -> mp.release()); } catch (Exception e){ e.printStackTrace(); Toast.makeText(window2.this, "Playback failed", Toast.LENGTH_SHORT).show(); } } }); } // Helper method to initialize MediaRecorder with consistent settings private void initMediaRecorder() { myAudioRecorder = new MediaRecorder(); myAudioRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); myAudioRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); myAudioRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); myAudioRecorder.setOutputFile(outputFile); } // Helper method to reset UI and recorder state on failure private void resetRecordingState() { if (myAudioRecorder != null) { try { myAudioRecorder.release(); } catch (Exception e) { e.printStackTrace(); } myAudioRecorder = null; } record.setEnabled(true); stop.setEnabled(false); play.setEnabled(false); } public void OpenWindow4(View view) { Spinner spinner = findViewById(R.id.spinner3); String SpinTxt = spinner.getSelectedItem().toString(); EditText edit2 = findViewById(R.id.editText); String editTextTxt = edit2.getText().toString(); Intent intent = new Intent(this,window4.class); intent.putExtra("THE_TEXT",editTextTxt); intent.putExtra("SPIN_CHOICE",SpinTxt); startActivity(intent); } public void OpenWindow4Speech(View view) { Spinner spinner = findViewById(R.id.spinner4); String SpinTxt = spinner.getSelectedItem().toString(); EditText edit2 = findViewById(R.id.editText2); String editTextTxt = edit2.getText().toString(); Intent intent = new Intent(this,window4.class); intent.putExtra("THE_RECORD",outputFile); intent.putExtra("SPIN_CHOICE",SpinTxt); intent.putExtra("THE_TEXT",editTextTxt); startActivity(intent); } @Override public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { String item = adapterView.getItemAtPosition(i).toString(); if (item.equals("S2T")) { Intent intent = new Intent(this,windows2B.class); startActivity(intent); } } @Override public void onNothingSelected(AdapterView<?> adapterView) { } }
Key Fixes Explained
- Consistent Output Format: We use
THREE_GPPeverywhere to match the.3gpfile extension, eliminating the start failure. - Error Handling: Added proper state reset in catch blocks to ensure buttons return to a valid state if recording fails.
- Resource Management: Added
release()calls for bothMediaRecorderandMediaPlayerto avoid resource leaks, and a helper methodinitMediaRecorder()to ensure consistent setup. - SharedPreferences Fix: Now we fetch the current value first, increment it, commit, then use the updated value for file naming.
- Stop Button Safety: Wrapped
stop()in a try-catch to handle cases where no recording is active, preventing the crash.
Additional Notes
Don't forget to request required permissions in your manifest and handle dynamic permissions for Android 6.0+:
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
内容的提问来源于stack exchange,提问作者SimpleNigal
相关产品推荐
相关产品推荐

