Android 錄音功能直接拿去用
先貼個效果圖給大家看一下,看看這個錄音包的功能
一、實現錄音的 Service
這個類可以說是這個包的核心了,如果理解了這個 Service,錄音這一塊基本就沒什麼問題了。
錄音主要是利用 MediaRecoder 這個類,進行聲音的記錄,接下來我們一起來看看具體的實現。
public class RecordingService extends Service { private String mFileName; private String mFilePath; private MediaRecorder mRecorder; private long mStartingTimeMillis; private long mElapsedMillis; @Override public int onStartCommand(Intent intent, int flags, int startId) { startRecording(); return START_STICKY; } @Override public void onDestroy() { if (mRecorder != null) { stopRecording(); } super.onDestroy(); } // 開始錄音 public void startRecording() { setFileNameAndPath(); mRecorder = new MediaRecorder(); mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); //錄音檔案儲存的格式,這裡儲存為 mp4 mRecorder.setOutputFile(mFilePath); // 設定錄音檔案的儲存路徑 mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); mRecorder.setAudioChannels(1); // 設定錄音檔案的清晰度 mRecorder.setAudioSamplingRate(44100); mRecorder.setAudioEncodingBitRate(192000); try { mRecorder.prepare(); mRecorder.start(); mStartingTimeMillis = System.currentTimeMillis(); } catch (IOException e) { Log.e(LOG_TAG, "prepare() failed"); } } // 設定錄音檔案的名字和儲存路徑 public void setFileNameAndPath() { File f; do { count++; mFileName = getString(R.string.default_file_name) + "_" + (System.currentTimeMillis()) + ".mp4"; mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath(); mFilePath += "/SoundRecorder/" + mFileName; f = new File(mFilePath); } while (f.exists() && !f.isDirectory()); } // 停止錄音 public void stopRecording() { mRecorder.stop(); mElapsedMillis = (System.currentTimeMillis() - mStartingTimeMillis); mRecorder.release(); getSharedPreferences("sp_name_audio", MODE_PRIVATE) .edit() .putString("audio_path", mFilePath) .putLong("elpased", mElapsedMillis) .apply(); if (mIncrementTimerTask != null) { mIncrementTimerTask.cancel(); mIncrementTimerTask = null; } mRecorder = null; } }
可以看到在 onStartCommand() 裡面有一個 startRecording() 方法,在外部啟動這個RecordingService 的時候,便會呼叫這個 startRecording() 方法開始錄音。
在 startRecording() 方法中先呼叫了 setFileNameAndPath 方法,初始化了錄音檔案的名字和儲存的路徑,為了讓每個錄音檔案都有唯一的名字,我呼叫System.currentMillis() 拼接到錄音檔案的名字裡面。
public void setFileNameAndPath() { File f; do { count++; mFileName = getString(R.string.default_file_name) + "_" + (System.currentTimeMillis()) + ".mp4"; mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath(); mFilePath += "/SoundRecorder/" + mFileName; f = new File(mFilePath); } while (f.exists() && !f.isDirectory()); }
設定好了檔案的名字和儲存路徑之後,對 mRecorder 進行一系列引數的設定,這個mRecorder 是 MediaRecorder 的一個例項,專門用於錄音的儲存。
mRecorder = new MediaRecorder(); mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); //錄音檔案儲存的格式,這裡儲存為 mp4 mRecorder.setOutputFile(mFilePath); // 設定錄音檔案的儲存路徑 mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); mRecorder.setAudioChannels(1); // 設定錄音檔案的清晰度 mRecorder.setAudioSamplingRate(44100); mRecorder.setAudioEncodingBitRate(192000); try { mRecorder.prepare(); mRecorder.start(); mStartingTimeMillis = System.currentTimeMillis(); } catch (IOException e) { Log.e(LOG_TAG, "prepare() failed"); }
設定好引數之後,啟動 mRecorder 開始錄音,可以看到啟動 mRecorder 開始錄音後,我還將當前的時間賦值給 mStartingTimeMills,這裡主要是為了記錄錄音的時長,等到錄音結束後再獲取一次當前的時間,然後將兩個時間進行相減,就能得到錄音的具體時長了。
等到錄音結束,停止服務後,便會回撥 RecordingService 的 onDestroy() 方法,這時候便會呼叫 stopRecording() 方法,關閉 mRecorder,並用 SharedPreferences 儲存錄音檔案的資訊,最後將 mRecorder 置空,防止記憶體洩露
public void stopRecording() { mRecorder.stop(); mElapsedMillis = (System.currentTimeMillis() - mStartingTimeMillis); mRecorder.release(); getSharedPreferences("sp_name_audio", MODE_PRIVATE) .edit() .putString("audio_name", mFileName) .putString("audio_path", mFilePath) .putLong("elpased", mElapsedMillis) .apply(); if (mIncrementTimerTask != null) { mIncrementTimerTask.cancel(); mIncrementTimerTask = null; } mRecorder = null; }
二、顯示錄音介面的 RecordAudioDialogFragment
使用者進行的時候,總不能讓 App 跳轉到另外一個介面吧,這樣使用者體驗並不是很好,比較好的方法是顯示一個對話方塊,讓使用者進行操作,既然要用對話方塊,必然離不開 DialogFragment。
public class RecordAudioDialogFragment extends DialogFragment { private boolean mStartRecording = true; long timeWhenPaused = 0; private FloatingActionButton mFabRecord; private Chronometer mChronometerTime; public static RecordAudioDialogFragment newInstance(int maxTime) { RecordAudioDialogFragment dialogFragment = new RecordAudioDialogFragment(); Bundle bundle = new Bundle(); bundle.putInt("maxTime", maxTime); dialogFragment.setArguments(bundle); return dialogFragment; } @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { Dialog dialog = super.onCreateDialog(savedInstanceState); final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); View view = getActivity().getLayoutInflater().inflate(R.layout.fragment_record_audio, null); mFabRecord.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(getActivity() , new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO}, 1); }else { onRecord(mStartRecording); mStartRecording = !mStartRecording; } } }); builder.setView(view); return builder.create(); } private void onRecord(boolean start) { Intent intent = new Intent(getActivity(), RecordingService.class); if (start) { File folder = new File(Environment.getExternalStorageDirectory() + "/SoundRecorder"); if (!folder.exists()) { folder.mkdir(); } mChronometerTime.setBase(SystemClock.elapsedRealtime()); mChronometerTime.start(); getActivity().startService(intent); } else { mChronometerTime.stop(); timeWhenPaused = 0; getActivity().stopService(intent); } } }
可以看到在 RecordAudioDialogFragment 有一個 newInstance(int maxTime) 的靜態方法供外部呼叫,如果想設定錄音的最大時長,直接傳引數進去就行了。
好的,敲黑板,重點來了,其實這個對話方塊的重點部分就是在 onCreateDialog()中,我們先載入了我們自定義的對話方塊的佈局,當點選錄音的按鈕的時候,先進行相關許可權的申請,這裡有個巨坑,錄音許可權 android.permission.RECORD_AUDIO 在不久前還是普通許可權的,不知道什麼時候突然變成了危險許可權,需要我們進行申請,Google 真是會玩。
public Dialog onCreateDialog(Bundle savedInstanceState) { Dialog dialog = super.onCreateDialog(savedInstanceState); final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); View view = getActivity().getLayoutInflater().inflate(R.layout.fragment_record_audio, null); mFabRecord.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(getActivity() , new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO}, 1); }else { onRecord(mStartRecording); mStartRecording = !mStartRecording; } } }); builder.setView(view); return builder.create(); }
申請好許可權之後便會呼叫 onRecord() 這個方法,然後將 boolean mStartRecording 進行反轉,這樣就不用寫難看的 if else 了,直接改變 mStartRecording 的值,然後在onRecord() 裡面進行處理
接下來看下 onRecord 幹了什麼
private void onRecord(boolean start) { Intent intent = new Intent(getActivity(), RecordingService.class); if (mStartRecording) { File folder = new File(Environment.getExternalStorageDirectory() + "/SoundRecorder"); if (!folder.exists()) { folder.mkdir(); } mChronometerTime.setBase(SystemClock.elapsedRealtime()); mChronometerTime.start(); getActivity().startService(intent); } else { mChronometerTime.stop(); timeWhenPaused = 0; getActivity().stopService(intent); } }
好吧,其實並沒有幹了什麼大事,只是建立了儲存錄音檔案的資料夾,然後根據mStartRecording 的值進行 RecordingService 的啟動和關閉罷了。在啟動時還順便開始了 mChronometer 的計時顯示,這是一個 Android 原生的顯示計時的一個控制元件。
三、播放錄音的 PlaybackDialogFragment
其實,如果只是錄音這一塊的話,寫個 MediaPlayer 就可以了,然而還要寫播放的時間進度,以及顯示一個稍微好看點的進度條,我能怎樣,我也很煩啊。
外部呼叫這個對話方塊的時候,只需要傳入一個包含錄音檔案資訊的 RecordingItem,因為包含的資訊比較多,所以最好將 RecordingItem 進行序列化。
public static PlaybackDialogFragment newInstance(RecordingItem item) { PlaybackDialogFragment fragment = new PlaybackDialogFragment(); Bundle bundle = new Bundle(); bundle.putParcelable(ARG_ITEM, item); fragment.setArguments(b); return fragment; }
好,重點又來了,來看看 onCreateDialog() 方法,在載入了佈局之後,給 mSeekBar 設定監聽,mSeekBar 是一個顯示進度條的控制元件,當開始播放錄音時候,將錄音檔案的時長,設定進 mSeekBar 裡面,播放錄音的同時,執行 mSeekBar,透過監聽 mSeekBar 的進度,重新整理顯示的播放進度。
public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); View view = getActivity().getLayoutInflater().inflate(R.layout.fragment_media_playback, null); mTvFileLength.setText(String.valueOf(mFileLength)); mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if(mMediaPlayer != null && fromUser) { mMediaPlayer.seekTo(progress); mHandler.removeCallbacks(mRunnable); long minutes = TimeUnit.MILLISECONDS.toMinutes(mMediaPlayer.getCurrentPosition()); long seconds = TimeUnit.MILLISECONDS.toSeconds(mMediaPlayer.getCurrentPosition()) - TimeUnit.MINUTES.toSeconds(minutes); mCurrentProgressTextView.setText(String.format("%02d:%02d", minutes,seconds)); updateSeekBar(); } else if (mMediaPlayer == null && fromUser) { prepareMediaPlayerFromPoint(progress); updateSeekBar(); } } }); mPlayButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onPlay(isPlaying); isPlaying = !isPlaying; } }); mTvFileLength.setText(String.format("%02d:%02d", minutes,seconds)); builder.setView(view); return builder.create(); }
當點選播放錄音的按鈕之後,會呼叫 onPlay() 方法,然後根據 isPlaying(標識當前是否播放錄音)的值,來呼叫不同的方法
private void onPlay(boolean isPlaying){ if (!isPlaying) { if(mMediaPlayer == null) { startPlaying(); //start from beginning } } else { pausePlaying(); } }
我們最關心的,莫過於 startPlaying() 這個方法,這個方法便是來開啟播放錄音的,我們首先將外部傳入的有關的錄音資訊,設定給 MediaPlayer,然後開始呼叫mMediaPlayer.start() 進行錄音的播放,然後呼叫 updateSeekbar() 實時更新進度條的內容。當 MediaPlayer 的內容播放完成後,呼叫 stopPlaying() 方法,關閉mMediaPlayer。
private void startPlaying() { mMediaPlayer = new MediaPlayer(); mMediaPlayer.setDataSource(item.getFilePath()); mMediaPlayer.prepare(); mSeekBar.setMax(mMediaPlayer.getDuration()); mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { mMediaPlayer.start(); } }); mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { stopPlaying(); } }); updateSeekBar(); }
作者:Android技術開發
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4686/viewspace-2821933/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 直接拿去用!每個App都會用到的LoadingLayoutAPP
- 如何實現前端錄音功能前端
- 直播原始碼,直接在動態介面播放錄音原始碼
- html5錄音功能實戰HTML
- WebRTC錄音功能 | 掘金技術徵文Web
- vue實現錄音功能(pc端)Vue
- 基於網頁呼叫錄音功能網頁
- TouchEvent實現前端錄音打分功能前端
- 搭建一個Java專案可直接拿去使用的通用工具類Java
- js實現網頁端錄音功能JS網頁
- Android音視訊之MediaRecorder音視訊錄製Android
- uniapp 實現打電話錄音功能APP
- win10有錄音功能嗎 win10系統錄音裝置在哪找Win10
- win10系統下怎麼使用錄音功能 win10系統下錄音功能的使用方法Win10
- 直接拿來用!10款實用Android UI工具AndroidUI
- Android音訊處理知識(一)MediaRecorder錄製音訊Android音訊
- Android音訊開發之AudioRecord錄音實現Android音訊
- Android 音訊應用框架Android音訊框架
- android IM模組-語音-錄製篇1Android
- android獲得手機照片,攝像,拍照,錄音等功能並將其轉為字串Android字串
- 音訊_錄音音訊
- Android多媒體之認識聲音、錄音與播放(PCM)Android
- 雲伺服器被黑客大佬登入拿去挖礦記錄!伺服器黑客
- Win10內建錄音機功能如何使用_win10自帶錄音機的使用教程Win10
- 直接用通訊錄Excel群發電子郵件Excel
- Android中實現錄製內建聲音Android
- H5實現移動端語音錄製功能H5
- Android WebView 實現檔案選擇、拍照、錄製視訊、錄音AndroidWebView
- 直接用通訊錄Excel群發電子郵件(轉)Excel
- Android 音樂播放器開發實錄(MediaSession)Android播放器Session
- Android 音視訊錄製硬編碼實現Android
- 用“稽核物件”功能記錄檔案刪除記錄物件
- 谷歌Stadia將上線語音功能,大量社交功能待啟用谷歌
- 直接通過瀏覽器開啟Android App 應用瀏覽器AndroidAPP
- android專案整體介面架構(可直接複用)Android架構
- 直接拿來用!最火的Android開源專案Android
- 原神周邊文字遊戲 拿去玩!遊戲
- 在Mac中如何啟用語音輸入功能?Mac