Used for recording audio and video

Common situations for recording audio works using MediaRecorder are as follows:

MediaRecorder recorder = new MediaRecorder(); recorder.setAudioSource(MediaRecorder.AudioSource.MIC); recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); recorder.setOutputFile(PATH_NAME); recorder.prepare(); recorder.start(); // Now start recording... recorder.stop(); recorder.reset(); // You can reuse the recorder.release() object by returning the setAudioSource () step; // Now the object cannot be reusedCopy the code
  • Number of audio channels: 1 (mono) or 2 (stereo).
  • Audio encoding bit rate (setAudioEncodingBitRate) : audio encoding bit rate in bits per second.
  • Audio sampling rate (SEtaos amplingrate) : The number of audio samples per second.
  • FPS (setCaptureRate) : Rate at which frames are captured in frames per second.
  • SetOrientationHint: Clockwise rotation Angle in degrees. The supported angles are 0, 90, 180, and 270 degrees.
  • SetVideoEncodingBitRate Indicates the video encoding bitrate in units of bits per second.
  • Video frame rate: The number of video frames captured per second
  • SetVideoSize: Width and height

The public class

public void addOnRoutingChangedListener (AudioRouting.OnRoutingChangedListener listener, 
                Handler handler)
Copy the code

Add AudioRouting. OnRoutingChangedListener routing change notification to receive the MediaRecorder.

public List<MicrophoneInfo> getActiveMicrophones ()
Copy the code

Returns a list of active microphones. By querying the channel map for each active microphone, developers can know how each channel or capture stream uses the microphone.

public AudioRecordingConfiguration getActiveRecordingConfiguration ()
Copy the code

Returns the current active audio recording of this recorder.

public static final int getAudioSourceMax ()
Copy the code

Gets the maximum number of audio sources.

public LogSessionId getLogSessionId ()
Copy the code

Return the MediaRecorder LogSessionId.

public int getMaxAmplitude ()
Copy the code

Returns the maximum absolute amplitude sampled since the last time this method was called. This function is called only after setAudioSource ().

public PersistableBundle getMetrics ()
Copy the code

Returns measurement data about the current Mediarecorder instance.

public AudioDeviceInfo getPreferredDevice ()
Copy the code

Returns setPreferredDevice (AudioDeviceInfo) the selected input device specified. Note that this is not guaranteed to correspond to the actual device used for recording.

public AudioDeviceInfo getRoutedDevice ()
Copy the code

Return the AudioDeviceInfo used to identify the current route to this MediaRecorder Note: The query is valid only if MediaRecorder is currently recording. If the logger is not recorded, the device returned may be empty or correspond to the device previously selected when the logger was last activated.

public Surface getSurface ()
Copy the code

Gets the surface from which to record when using a surface video source.

Can only be called after prepare (). Frames rendered to the surface before start () are discarded.

public boolean isPrivacySensitive ()
Copy the code

Returns whether this MediaRecorder is marked as privacy-sensitive in audio capture.

public void pause ()
Copy the code

Pause the recording. This function is called after start (). You can resume recording without reconfiguring using resume (), as opposed to stop (). If recording is paused, no action is performed. When the recording is paused and resumed, the resulting output immediately switches to the resumed scene as if nothing had happened during the pause.

public void prepare ()
Copy the code

Prepare loggers to begin capturing and encoding data. This method must be called after setting up the required audio and video sources, encoders, file formats, and so on, but before start ().

public void registerAudioRecordingCallback (Executor executor, 
                AudioManager.AudioRecordingCallback cb)
Copy the code

Register a callback to via AudioManager. AudioRecordingCallback audio capture change notice. When capturing path configuration changes (preprocessing, format, sampling rate…) Or capture is muted/unmuted by the system, a callback is received.

public void release ()
Copy the code

Releases resources associated with this MediaRecorder object. It is a good practice to call this method after using MediaRecorder. In particular, this method should be called to release the MediaRecorder object whenever an activity of the application is paused (calling its onPause () method) or stopped (calling its onStop () method), unless the application specifically needs to keep the object. In addition to preserving unnecessary resources (such as memory and codec instances), if the MediaRecorder object is no longer needed, if this method is not called immediately, it may cause the mobile device to continuously drain battery, and other applications to fail to record if multiple instances of the same codec are not supported on the device. Even if multiple instances of the same codec are supported, using multiple instances unnecessarily at the same time can lead to performance degradation.

public void removeOnRoutingChangedListener (AudioRouting.OnRoutingChangedListener listener)
Copy the code

Delete AudioRouting OnRoutingChangedListener, this file has been added to reroute notify previously.

public void reset ()
Copy the code

Restart MediaRecorder to its idle state. After calling this method, you must configure it again as if it had just been constructed.

public void resume ()
Copy the code

Resume recording. This function is called after start (). If recording is not paused, no action is performed.

public void setAudioChannels (int numChannels)
Copy the code

Sets the number of audio channels to record. This method is called before prepare (). Prepare () performs additional checks on the parameters to ensure that a specified number of audio channels are applicable.

Usually it is 1 (mono) or 2 (stereo).

public void setAudioEncoder (int audio_encoder)
Copy the code

Sets the audio encoder used for recording. If this method is not called, the output file will contain no tracks. Call this function after setOutputFormat () but before prepare ().

public void setAudioEncodingBitRate (int bitRate)
Copy the code

Sets the recorded audio encoding bit rate. This method is called before prepare (). Prepare () can perform additional checks on parameters to ensure that the specified bit rate is applicable, and sometimes the passed bit rate is internally clipped to ensure that audio recording can proceed smoothly depending on the platform’s capabilities.

public void setAudioProfile (EncoderProfiles.AudioProfile profile)
Copy the code

Record using the Settings in the audio profile. This method should be called after setting the video and audio sources and before setOutputFile (). This method can be used instead of setProfile(CamcorderProfile) when using encoder profiles

public void setAudioSamplingRate (int samplingRate)
Copy the code

Set the audio sampling rate for recording. This method is called before prepare (). Prepare () performs additional checks on the parameters to ensure that the specified audio sampling rate is applicable. The sampling rate really depends on the format of the audio recording and the capabilities of the platform. For example, the AAC audio coding standard supports sampling rates ranging from 8 to 96 kHz, AMRNB supports sampling rates of 8 kHz, and AMRWB supports sampling rates of 16 kHz. For supported audio sampling rates, consult the relevant audio encoding standard.

public void setAudioSource (int audioSource)
Copy the code

Sets the audio source to use for recording. If this method is not called, the output file will contain no tracks. Before setting recording parameters or encoders, you need to specify the source. This function is called only before setOutputFormat ().

public void setCamera (Camera c)
Copy the code

Set up the camera for recording.

Use this feature to quickly switch between preview and capture modes without removing the camera object. To do so, call camera.unlock (). Must be called before prepare ().

This method is deprecated at API level 21.

Use getSurface () and the Android.hardware.camera2 API instead.

public void setCaptureRate (double fps)
Copy the code

Set the video frame capture rate. This can be used to set a video frame capture rate that is different from the playback rate of the recorded video. This method also sets the recording mode to Delay. In time-lapse video, only the video is recorded. If the application sets audio related parameters, they are ignored when a delayed recording session starts.

public void setInputSurface (Surface surface)
Copy the code

Loggers are configured to use persistent surfaces when using surface video sources.

Can only be called before prepare (). If called, getSurface () should not be used and an IllegalStateException will be thrown. Frames rendered to the surface before start () are discarded.

public void setLocation (float latitude,  float longitude)
Copy the code

Set and store geographic data (latitude and longitude) in the output file. This method should be called before prepare (). If the OutputFormat is outputformat.three_gpp or outputformat.mpeg_4, the geographic data is stored in the udta box and ignored for other output formats. Geographic data is stored according to the ISO-6709 standard.

public void setLogSessionId (LogSessionId id)
Copy the code

Set the MediaRecorder LogSessionId.

public void setMaxDuration (int max_duration_ms)
Copy the code

Sets the maximum duration (milliseconds) for recording sessions. Call this function after setOutputFormat () but before prepare (). Recorded after reaching a specified duration, we will send the MediaRecorder. OnInfoListener notifications, which contains “what” code media MEDIA_RECORDER_INFO_MAX_DURATION_REACHED has been attained, and recording will stop. Stopping occurs asynchronously and there is no guarantee that the logger has stopped by the time the listener is notified.

When using mpeg-4 containers (setOutputFormat (int) and OutputFormat#MPEG_4), it is recommended to set the maximum duration suitable for use cases. Setting the duration greater than required may cause the output file to take longer than required because MOOV Box reserves space because it requires a large amount of movie data for this recorded session. The unused space of the moov-box becomes a free box in the output file.

public void setMaxFileSize (long max_filesize_bytes)
Copy the code

Sets the maximum file size, in bytes, for recording sessions. Call this function after setOutputFormat () but before prepare (). When the recording reaches the specified file size, a notification is sent to the MediaRecorder. OnInfo Listener containing the MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED with the code “what” and the recording will stop. Stopping occurs asynchronously and there is no guarantee that the logger has stopped by the time the listener is notified.

When using mpeg-4 containers (setOutputFormat (int) and OutputFormat#MPEG_4), it is recommended to set the maximum file size appropriate for use cases. Setting the file size larger than the desired size may cause the output file to be larger than the desired size because MOOV Box requires a large amount of movie data for this recorded session and reserves space. The unused space of the moov-box becomes a free box in the output file.

public void setNextOutputFile (File file)
Copy the code

Sets the next output file to use when the previous output reached the maximum file size. Files should be searchable. Once the next output file is set, the application should not use it until stop (). The application must call this function after receiving the “What” code of media MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING on the MediaRecorder. OnInfo listener, Then the “what” code of MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED can be received. This file is not used until you switch to the output. When the next output file is used, the application will receive the media MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED. If the previous output file is not in use, the application will not be able to set up a new output file. The application is responsible for cleaning up unused files after a call to stop ().

public void setNextOutputFile (FileDescriptor fd)
Copy the code

Sets the next output file descriptor to use when the last output setOutputFile or setNextOutputFile reaches the maximum file size. File descriptors must be searchable and writable. After the next output file is set, the application should not use the file referenced by this file descriptor until stop (). The application is responsible for closing the file descriptor. As long as the call returns, you can safely do this. The application must call this function after receiving the “What” code of media MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING on the MediaRecorder. OnInfo listener, Then the “what” code of MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED can be received. This file is not used until you switch to the output. When the next output file is used, the application will receive the media MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED. If the previous output file is not in use, the application will not be able to set up a new output file. The application is responsible for cleaning up unused files after a call to stop ().

public void setOnErrorListener (MediaRecorder.OnErrorListener l)
Copy the code

Registers callbacks that are called when an error occurs during recording.

public void setOnInfoListener (MediaRecorder.OnInfoListener listener)
Copy the code

Registers the callback to be invoked when an informational event occurs during recording.

public void setOrientationHint (int degrees)
Copy the code

Set the direction prompt for output video playback. This method should be called before prepare (). This method does not trigger rotation of the source video frame during video recording. Instead, in the case of OutputFormat outputformat.three_gpp or outputformat.mpeg_4, a composition matrix containing rotation angles is added to the output video so that the video player can select the appropriate playback direction. Note that some video players may choose to ignore the compositing matrix in the video during playback.

public void setOutputFile (FileDescriptor fd)
Copy the code

Pass in the file descriptor for the file to be written. Call this function after setOutputFormat () but before prepare ().

public void setOutputFile (String path)
Copy the code

Sets the path to the output file to generate. Call this function after setOutputFormat () but before prepare ().

public void setOutputFile (File file)
Copy the code

Pass in the file object to write to. Call this function after setOutputFormat () but before prepare (). Files should be searchable. Once the next output file is set, the application should not use it until stop (). The application is responsible for cleaning up unused files after a call to stop ().

public void setOutputFormat (int output_format)
Copy the code

Format the output file generated during recording. Call this function after setAudioSource () /setVideoSource () but before prepare ().

When using H.263 video encoders and AMR audio encoders, it is recommended to always use the 3GP format. Using the MPEG-4 container format can be confusing for some desktop players.

public boolean setPreferredDevice (AudioDeviceInfo deviceInfo)
Copy the code

Specifies the audio device (via the AudioDeviceInfo object) to route input from this MediaRecorder.

public boolean setPreferredMicrophoneDirection (int direction)
Copy the code

Specifies the logical microphone (for processing).

public boolean setPreferredMicrophoneFieldDimension (float zoom)
Copy the code

Specifies the scale factor (that is, the field size) for the selected microphone (for processing). The microphone selected is determined by the use case for the flow.

public void setPreviewDisplay (Surface sv)
Copy the code

Sets the surface to display a preview of the recorded media (video). Call this function before prepare () to ensure that the desired preview display is set. If the setCamera (Android.hardware.Camera) is used and the surface is set as the Camera, the application does not need to call this function. If you call this function with a non-empty surface, the camera’s preview surface is replaced with the new surface. If this method is called with an empty surface or not at all, Media Recorder will not change the camera’s preview surface.

public void setPrivacySensitive (boolean privacySensitive)
Copy the code

Indicates that this capture request is privacy-sensitive and does not allow any concurrent capture.

The default Settings do not discriminate between privacy unless the audio source set using setAudioSource (int) is AudioSource#VOICE#COMMUNICATION or AudioSource#CAMCORDER.

Always takes precedence over the default value of the audio source when explicitly set.

This API is only allowed if the audio source is one of the following:

  • AudioSource#MIC AudioSource#MIC

  • AudioSource#CAMCORDER AudioSource# camera

  • AudioSource#VOICE_RECOGNITION AudioSource# voice # recognition

  • AudioSource#VOICE_COMMUNICATION

  • AudioSource#UNPROCESSED AudioSource#

  • AudioSource#VOICE_PERFORMANCE AudioSource# voice # performance

If this condition is not met, a call to prepare () raises IOException.

Must be called after setAudioSource (int) and before setOutputFormat (int).

public void setProfile (CamcorderProfile profile)
Copy the code

Record using the Settings in the CamcorderProfile object. This method should be called after setting the video and audio sources and before setOutputFile (). If you use a time-lapse camera profile, the source or recording parameters associated with audio are ignored.

public void setVideoEncoder (int video_encoder)
Copy the code

Sets the video encoder for recording. If this method is not called, the output file will contain no video tracks. Call this function after setOutputFormat () and before prepare ().

public void setVideoEncodingBitRate (int bitRate)
Copy the code

Set the recorded video encoding bit rate. This method is called before prepare (). Prepare () can perform additional checks on parameters to ensure that the specified bit rate is applicable, and sometimes the passed bit rate is internally clipped to ensure that video recording can proceed smoothly depending on the platform’s capabilities.

Note: Actual bit rates and other coding features may be affected by the lowest-quality floor behavior introduced in build.version_codes.s. More details on how and where this affects video coding can be found on the MediaCodec page and in the “Quality Bottom Line” near the top of the page.

public void setVideoEncodingProfileLevel (int profile, int level)
Copy the code

Sets the video encoding profile and level required for recording. The configuration file and level must be valid for the video coder set by setVideoEncoder (int). This method can be called before or after setVideoEncoder (int), but must be called before prepare (). Prepare () can perform additional checks on the parameters to ensure that the specified profile and level are applicable, and sometimes the passed profile or level will be discarded due to codec capabilities or to ensure that video recording can proceed smoothly based on the platform’s capabilities.

. Applications can also use MediaCodeInfo CodeCapabilities# ProfileLevel query format applicable configuration files and corresponding level combination. Please note that the codec actually used by this MediaRecorder instance may not support the requested profile/level.

public void setVideoFrameRate (int rate)
Copy the code

Sets the frame rate of the video to capture. Must be called after setVideoSource (). Call this function after setOutputFormat () but before prepare ().

public void setVideoProfile (EncoderProfiles.VideoProfile profile)
Copy the code

Record using the Settings in the VideoProfile object.

This method should be called after setting the video and audio sources and before setOutputFile ().

This method can be used instead of setProfile (CamcorderProfile) when using an encoder profile.

public void setVideoSize (int width, int height)
Copy the code

Sets the width and height of the video to capture. Must be called after setVideoSource (). Call this function after setOutputFormat () but before prepare ().

public void setVideoSource (int video_source)
Copy the code

Sets the video source to be used for recording. If this method is not called, the output file will contain no video tracks. Before setting recording parameters or encoders, you need to specify the source. This function is called only before setOutputFormat ().

public void start ()
Copy the code

Start capturing data and encoding it into the file specified with setOutputFile (). This function is called after prepare ().

Since the API level is 13, if the application sets the camera through setCamera (Android.hardware.camera), the application can use the camera after this method is called. These apps don’t need to lock the camera again. However, if this method fails, the application should still lock the camera. The application should not start another recording session during recording.

public void stop ()
Copy the code

Stop recording. This function is called after start (). Once you stop recording, you will have to reconfigure it as if it had just been built. Note that if stop () is called without valid audio/video data, a RuntimeException is intentionally thrown to the application. This happens if stop () is called immediately after start (). This failure allows the application to take appropriate steps to clean up the output file (for example, delete the output file) because the output file is incorrectly constructed when this occurs.

public void unregisterAudioRecordingCallback (AudioManager.AudioRecordingCallback cb)
Copy the code

Logout registerAudioRecordingCallback before (Java. Util. Concurrent. The Executor, Android. Media. AudioManager. AudioRecordingCallback) registered recording callback.

The Android multimedia framework supports capturing and encoding a variety of common audio and video formats. If the device hardware supports it, you can use the MediaRecorder API.

This document describes how to use MediaRecorder to write an application that captures audio from the device microphone, saves the audio, and plays it back using MediaPlayer. To record video, you need to use the device’s camera and MediaRecorder.

Note: The Android emulator cannot record audio. Be sure to test your code on a real device that can record audio.

Request permission to record audio

 <uses-permission android:name="android.permission.RECORD_AUDIO" />
Copy the code

Create and run MediaRecorder

Initialize a new instance of MediaRecorder:

  • usesetAudioSource()Set the audio source. You might useMIC.

Note: Most audio sources (including DEFAULT) process the audio signal. To record raw audio, select UNPROCESSED. Some devices do not support raw input. Please verify that audiomanager.getProperty (Audiomanager.property_Support_audio_source_unprocessed) is available first. If not, try VOICE_RECOGNITION. VOICE_RECOGNITION does not use AGC or noise suppression. You can use UNPROCESSED as an audio source even when this property is not supported, but there is no guarantee that the signal will go UNPROCESSED in that case.

  • usesetOutputFormat()Set the output file format. Please note that starting with Android 8.0 (API level 26),MediaRecorderSupport for MPEG2_TS format, which is useful for streaming:
 mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS);
Copy the code
  • usesetOutputFile()Set the output file name. You must specify the file descriptor that represents the actual file.
  • usesetAudioEncoder()Set the audio encoder.
  • By calling theprepare()The initialization is complete.

Start and stop the recording function by calling start() and stop(), respectively.

After using the MediaRecorder instance, call Release () to release its resources as soon as possible.

Note: On devices running Android 9 (API level 28) or higher, apps running in the background will not be able to access the microphone. Therefore, your application should record audio only when it is in the foreground, or when you add a MediaRecorder instance to the foreground service.

Use MediaMuxer to record multiple channels

Starting with Android 8.0 (API level 26), you can use Media Uxer to record multiple synchronized audio and video streams. In lower versions of Android, you can only record one audio track and/or one video track at a time.

Multiple tracks can be mixed together using the addTrack() method.

You can also add one or more metadata tracks with custom information for each frame, but only to the MP4 container. Your application defines the format and content of the metadata.

Adding metadata

Metadata is useful for offline processing. For example, data captured from a gyroscope sensor can be used to perform video stabilization.

When you add metadata tracks, the MIME format of the track must begin with the prefix application/. Writing metadata is the same as writing video or audio data, except that instead of the data coming from MediaCodec, the application passes a ByteBuffer with an associated timestamp to the writeSampleData() method. The timestamp must have the same time base as the video and audio tracks.

The resulting MP4 file uses TextMetaDataSampleEntry defined in section 12.3.3.2 of the ISO BMFF specification to indicate the MIME format of the metadata. When you use MediaExtractor to extract a file that contains the metadata track, the MIME format of the metadata is displayed as an instance of MediaFormat.

Code sample

The MediaRecorder example shows how to use the MediaRecorder and Camera APIS for video recording.

The following Activity example shows how to record an audio file using MediaRecorder. It also uses MediaPlayer to play audio.

KotlinJava

package com.android.audiorecordtest; import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; import android.media.MediaPlayer; import android.media.MediaRecorder; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.LinearLayout; import java.io.IOException; public class AudioRecordTest extends AppCompatActivity { private static final String LOG_TAG = "AudioRecordTest"; private static final int REQUEST_RECORD_AUDIO_PERMISSION = 200; private static String fileName = null; private RecordButton recordButton = null; private MediaRecorder recorder = null; private PlayButton playButton = null; private MediaPlayer player = null; // Requesting permission to RECORD_AUDIO private boolean permissionToRecordAccepted = false; private String [] permissions = {Manifest.permission.RECORD_AUDIO}; @Override 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(); } private void onRecord(boolean start) { if (start) { startRecording(); } else { stopRecording(); } } private void onPlay(boolean start) { if (start) { startPlaying(); } else { stopPlaying(); } } private void startPlaying() { player = new MediaPlayer(); try { player.setDataSource(fileName); player.prepare(); player.start(); } catch (IOException e) { Log.e(LOG_TAG, "prepare() failed"); } } private void stopPlaying() { player.release(); player = null; } private void startRecording() { recorder = new MediaRecorder(); recorder.setAudioSource(MediaRecorder.AudioSource.MIC); recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); recorder.setOutputFile(fileName); recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); try { recorder.prepare(); } catch (IOException e) { Log.e(LOG_TAG, "prepare() failed"); } recorder.start(); } private void stopRecording() { recorder.stop(); recorder.release(); recorder = null; } class RecordButton extends Button { boolean mStartRecording = true; OnClickListener clicker = new OnClickListener() { public void onClick(View v) { onRecord(mStartRecording); if (mStartRecording) { setText("Stop recording"); } else { setText("Start recording"); } mStartRecording = ! mStartRecording; }}; public RecordButton(Context ctx) { super(ctx); setText("Start recording"); setOnClickListener(clicker); } } class PlayButton extends Button { boolean mStartPlaying = true; OnClickListener clicker = new OnClickListener() { public void onClick(View v) { onPlay(mStartPlaying); if (mStartPlaying) { setText("Stop playing"); } else { setText("Start playing"); } mStartPlaying = ! mStartPlaying; }}; public PlayButton(Context ctx) { super(ctx); setText("Start playing"); setOnClickListener(clicker); } } @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); // Record to the external cache directory for visibility fileName = getExternalCacheDir().getAbsolutePath(); fileName += "/audiorecordtest.3gp"; ActivityCompat.requestPermissions(this, permissions, REQUEST_RECORD_AUDIO_PERMISSION); LinearLayout ll = new LinearLayout(this); recordButton = new RecordButton(this); ll.addView(recordButton, new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0)); playButton = new PlayButton(this); ll.addView(playButton, new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0)); setContentView(ll); } @Override public void onStop() { super.onStop(); if (recorder ! = null) { recorder.release(); recorder = null; } if (player ! = null) { player.release(); player = null; }}}Copy the code