
Android多媒体框架支持音频记录和播放。 在本文中,我将展示如何开发一个简单的录制应用程序,该应用程序将录制音频并将其使用Android SDK中的MediaRecorder
存储在Android设备的本地存储中。
您还将学习如何实时向用户请求权限,以及如何使用Android设备的本地存储。
用户界面创建
首先,我们需要创建一个录音接口。 这是一个简单的三按钮布局,将用于开始,暂停/继续和停止录制。
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/textview_sound_recorder_heading" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Sound Recorder" android:layout_centerHorizontal="true" android:textSize="32dp" android:textStyle="bold" android:textColor="#000" android:layout_marginTop="32dp"/> <Button android:id="@+id/button_start_recording" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Start" android:layout_alignParentBottom="true" android:layout_marginLeft="32dp" android:layout_marginBottom="32dp" android:layout_centerVertical="true"/> <Button android:id="@+id/button_pause_recording" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Pause" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="32dp"/> <Button android:id="@+id/button_stop_recording" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Stop" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_marginBottom="32dp" android:layout_marginRight="32dp"/> </RelativeLayout>
要求权限
创建用户界面后,我们可以开始使用MediaRecorder
来实现应用程序的基本功能。 但是首先,我们需要请求必要的权限才能录制音频和访问本地存储。 我们将在AndroidManifest.xml
文件中使用几行简单的代码来完成此操作:
<uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
您还需要检查用户是否已批准权限,然后才能使用MediaRecorder
。 让我们在Activity MainActivity.kt
:
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { val permissions = arrayOf(android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE) ActivityCompat.requestPermissions(this, permissions,0) }
注意:稍后,这些代码行将被移至音频录制开始按钮的OnClickListener
以便我们确保没有必要的权限就不会MediaRecorder
。
录制并保存音频
添加OnClickListeners
将侦听器添加到按钮,以便它们响应用户事件。 正如我前面提到的,将向音频录制开始按钮的OnClickListener添加对必要权限的检查:
button_start_recording.setOnClickListener { if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { val permissions = arrayOf(android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE) ActivityCompat.requestPermissions(this, permissions,0) } else { startRecording() } } button_stop_recording.setOnClickListener{ stopRecording() } button_pause_recording.setOnClickListener { pauseRecording() }
接下来,我们需要指定保存音频的路径并配置MediaRecorder。
private var output: String? = null private var mediaRecorder: MediaRecorder? = null private var state: Boolean = false private var recordingStopped: Boolean = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) output = Environment.getExternalStorageDirectory().absolutePath + "/recording.mp3" mediaRecorder = MediaRecorder() mediaRecorder?.setAudioSource(MediaRecorder.AudioSource.MIC) mediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) mediaRecorder?.setAudioEncoder(MediaRecorder.AudioEncoder.AAC) mediaRecorder?.setOutputFile(output) }
我们将路径转到外部存储的根目录,并在其中添加记录名称和文件类型。 之后,我们创建一个MediaRecorder
对象,并定义用于记录的声源,音频MediaRecorder
,格式和文件。
录制并保存音频
用于启动MediaRecorder
的代码OnClickListener
音频录制开始按钮的MediaRecorder
中定义:
private fun startRecording() { try { mediaRecorder?.prepare() mediaRecorder?.start() state = true Toast.makeText(this, "Recording started!", Toast.LENGTH_SHORT).show() } catch (e: IllegalStateException) { e.printStackTrace() } catch (e: IOException) { e.printStackTrace() } }
如您所见,在开始录制之前,我们需要调用prepare
函数。 我们还将调用嵌入到try-catch块中,以便在prepare
函数失败时应用程序不会中断。
停止录制按钮OnClickListeners
与上面的代码非常相似。
private fun stopRecording() { if (state) { mediaRecorder?.stop() mediaRecorder?.release() state = false } else { Toast.makeText(this, "You are not recording right now!", Toast.LENGTH_SHORT).show() } }
在这里,我们在停止记录之前检查MediaRecorder
当前是否正在MediaRecorder
,因为如果在MediaRecorder
无法MediaRecorder
调用stop
方法,则应用程序将中断。 之后,我们将状态变量更改为false
以便用户无法再次按下停止按钮。
我们仍然需要为“暂停/恢复”按钮定义OnClickListener
。
@SuppressLint("RestrictedApi", "SetTextI18n") @TargetApi(Build.VERSION_CODES.N) private fun pauseRecording() { if (state) { if (!recordingStopped) { Toast.makeText(this,"Stopped!", Toast.LENGTH_SHORT).show() mediaRecorder?.pause() recordingStopped = true button_pause_recording.text = "Resume" } else { resumeRecording() } } } @SuppressLint("RestrictedApi", "SetTextI18n") @TargetApi(Build.VERSION_CODES.N) private fun resumeRecording() { Toast.makeText(this,"Resume!", Toast.LENGTH_SHORT).show() mediaRecorder?.resume() button_pause_recording.text = "Pause" recordingStopped = false }
在这两种方法中,我们检查MediaRecorder
是否MediaRecorder
。 如果有效,我们将暂停录制并将按钮文本更改为恢复。 再按一次将恢复录制。
最后,我们可以通过打开recording.mp3
文件来录制并收听音频,该文件将保存在我们的本地存储中。 只需打开文件资源管理器,然后对文件名recording.mp3
进行搜索。
源代码
这是我们应用程序的完整源代码:
package com.example.android.soundrecorder import android.Manifest import android.annotation.SuppressLint import android.annotation.TargetApi import android.content.pm.PackageManager import android.media.MediaRecorder import android.os.Build import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.os.Environment import android.support.v4.app.ActivityCompat import android.support.v4.content.ContextCompat import android.widget.Toast import kotlinx.android.synthetic.main.activity_main.* import java.io.IOException class MainActivity : AppCompatActivity() { private var output: String? = null private var mediaRecorder: MediaRecorder? = null private var state: Boolean = false private var recordingStopped: Boolean = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) mediaRecorder = MediaRecorder() output = Environment.getExternalStorageDirectory().absolutePath + "/recording.mp3" mediaRecorder?.setAudioSource(MediaRecorder.AudioSource.MIC) mediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) mediaRecorder?.setAudioEncoder(MediaRecorder.AudioEncoder.AAC) mediaRecorder?.setOutputFile(output) button_start_recording.setOnClickListener { if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { val permissions = arrayOf(android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE) ActivityCompat.requestPermissions(this, permissions,0) } else { startRecording() } } button_stop_recording.setOnClickListener{ stopRecording() } button_pause_recording.setOnClickListener { pauseRecording() } } private fun startRecording() { try { mediaRecorder?.prepare() mediaRecorder?.start() state = true Toast.makeText(this, "Recording started!", Toast.LENGTH_SHORT).show() } catch (e: IllegalStateException) { e.printStackTrace() } catch (e: IOException) { e.printStackTrace() } } @SuppressLint("RestrictedApi", "SetTextI18n") @TargetApi(Build.VERSION_CODES.N) private fun pauseRecording() { if (state) { if (!recordingStopped) { Toast.makeText(this,"Stopped!", Toast.LENGTH_SHORT).show() mediaRecorder?.pause() recordingStopped = true button_pause_recording.text = "Resume" } else { resumeRecording() } } } @SuppressLint("RestrictedApi", "SetTextI18n") @TargetApi(Build.VERSION_CODES.N) private fun resumeRecording() { Toast.makeText(this,"Resume!", Toast.LENGTH_SHORT).show() mediaRecorder?.resume() button_pause_recording.text = "Pause" recordingStopped = false } private fun stopRecording(){ if (state) { mediaRecorder?.stop() mediaRecorder?.release() state = false } else { Toast.makeText(this, "You are not recording right now!", Toast.LENGTH_SHORT).show() } } }
结论
现在,您知道MediaRecorder
工作方式,如何请求实时权限以及为什么这样做很重要。 您还了解了Android设备的本地存储以及如何在其中存储数据。
Github上提供了此应用程序的更复杂版本,它具有一些其他功能,例如使用MediaPlayer
播放录音。