You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android开发:MediaRecorder.stop()调用致应用崩溃求助

Fix for MediaRecorder Crash on Stop Button Click

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

  1. Mismatched Output Format & File Extension
    You initialized the recorder with OutputFormat.MPEG_4 but switched to THREE_GPP during re-recording, while your output file uses a .3gp extension (which only aligns with THREE_GPP). This conflict triggers the start failed: -38 error, meaning the recorder never actually starts recording.
  2. Unhandled Start Failure State
    When start() fails, you catch the exception but don't reset the button states. This leaves the stop button enabled even though no recording is active—clicking it triggers an invalid state crash.
  3. Improper MediaRecorder Resource Management
    Setting myAudioRecorder = null after stopping skips proper resource release, and your re-recording logic doesn't account for failed initial attempts.
  4. 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_GPP everywhere to match the .3gp file 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 both MediaRecorder and MediaPlayer to avoid resource leaks, and a helper method initMediaRecorder() 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:10:07