MediaRecorder实现微信、QQ、人人、易信等语音录制功能工具:MediaUtilAPI

2016-03-01 14:54 阅读 6,102 次 评论 0 条
版权声明:本文著作权归TeachCourse所有,未经许可禁止转载,谢谢支持!
转载请注明出处:http://teachcourse.cn/1514.html

摘要

本文介绍使用MediaRecorder进行录制音频、录制视频学习,熟悉MediaRecorder执行流程,通过简单的Demo结合解释运行效果,最后封装MediaRecorder的API工具,实现常见比如:微信、QQ、易信、人人、支付宝等录音功能模块开发,最后测试效果。

目录:

  • MediaRecorder概念
  • 执行流程图
  • 简单Demo介绍
  • 封装API
    getMaxAmplitude()方法介绍
    MediaUtil封装类工具
    类工具使用介绍
    测试

概念

MediaRecorder录制音频、视频文件时,需要注意:Android模拟器不具备录制音频的功能,在具备录音功能的真机测试,下面是使用MediaRecorder通用的开发步骤:

1、创建一个MediaRecorder实例

2、设置音频源setAudioSource(),你可能使用MediaRecorder.AudioSource.MIC

3、设置输出文件格式,用MediaRecorder.setOutputFormat()

4、设置输出文件名,用MediaRecorder.setOutputFile()

5、设置音频编码,用MediaRecorder.setAudioEncoder()

6、回调MediaRecorder.prepare()方法

7、开始录制音频MediaRecorder.start()

8、停止录制音频MediaRecorder.stop()

9、当你使用MediaRecorder实例录制音频回调MediaRecorder.release()方法立即释放资源

下面是通用的配置代码:

  1. MediaRecorder recorder = new MediaRecorder();  
  2. recorder.setAudioSource(MediaRecorder.AudioSource.MIC);  
  3. recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);  
  4. recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);  
  5. recorder.setOutputFile(PATH_NAME);  
  6. recorder.prepare();  
  7. recorder.start();   // Recording is now started  
  8. ...  
  9. recorder.stop();  
  10. recorder.reset();   // You can reuse the object by going back to setAudioSource() step  
  11. recorder.release(); // Now the object cannot be reused  

MediaRecorder在录制时采用注册监听器的方式获得更新或异常错误的事件消息,比如:setOnInfoListener(OnInfoListener)、setOnErrorListener(OnErrorListener),接收与监听相关的回调方法,MediaRecorder需要运行在循环的Thread线程中,默认UI Thread是循环的线程。

执行流程图

mediarecorder_state_diagram

详情参考:官方文档

简单Demo介绍

布局文件

布局文件放置两个按钮,一个录制音频按钮,点击开始录制,再次点击停止录制;一个播放按钮,点击播放,再次点击停止播放,代码如下:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     tools:context="${relativePackage}.${activityClass}" >  
  6. <TextView  
  7.     android:layout_width="wrap_content"  
  8.     android:layout_height="wrap_content"  
  9.     android:layout_above="@+id/record_autio"  
  10.     android:text="@string/hello_world"  
  11.     android:layout_centerHorizontal="true"  
  12.     android:layout_marginBottom="20dp"  
  13.     android:id="@+id/system_time"/>  
  14.     <Button  
  15.         android:id="@+id/record_autio"  
  16.         android:layout_width="wrap_content"  
  17.         android:layout_height="wrap_content"  
  18.         android:layout_centerInParent="true"  
  19.         android:text="点击录音" />  
  20.     <Button  
  21.         android:id="@+id/play_autio"  
  22.         android:layout_width="wrap_content"  
  23.         android:layout_height="wrap_content"  
  24.         android:layout_below="@+id/record_autio"  
  25.         android:layout_centerHorizontal="true"  
  26.         android:text="点击播放" />  
  27.   
  28. </RelativeLayout>  

MainActivity设计

MainActivity的代码比较简单,直接看就可以了,在设计上尽量让代码之间的耦合性降低,写了onRecord()和onPlay()方法,根据传入的value分别执行startRecording()、stopRecording()方法,在按钮点击后处理状态的变化,代码如下:

  1. package cn.teachcourse.activity;  
  2.   
  3. import android.app.Activity;  
  4. import android.media.MediaPlayer;  
  5. import android.media.MediaRecorder;  
  6. import android.os.Bundle;  
  7. import android.os.Environment;  
  8. import android.util.Log;  
  9. import android.view.View;  
  10. import android.view.View.OnClickListener;  
  11. import android.widget.Button;  
  12. import android.widget.TextView;  
  13.   
  14. import java.io.File;  
  15. import java.io.IOException;  
  16.   
  17. public class MainActivity extends Activity implements OnClickListener {  
  18.     private static final String LOG_TAG = "AudioRecordTest";  
  19.   
  20.     private Button mRecordButton;  
  21.     private Button mPlayButton;  
  22.     private TextView mSysTime_TV;  
  23.     private String mSavePath=null;  
  24.     private File mRootPath;  
  25.     private MediaRecorder mMediaRecorder;  
  26.     private MediaPlayer mPlayer;  
  27.     boolean mStartRecording=true;  
  28.     boolean mStartPlaying=true;  
  29.   
  30.     @Override  
  31.     protected void onCreate(Bundle savedInstanceState) {  
  32.         super.onCreate(savedInstanceState);  
  33.         setContentView(R.layout.activity_main);  
  34.         initView();  
  35.   
  36.         mRootPath = Environment.getExternalStorageDirectory();  
  37.         mSavePath=mRootPath.getAbsolutePath()+ "/recordAudio.3gp";  
  38.     }  
  39.   
  40.     private void initView() {  
  41.         mRecordButton = (Button) findViewById(R.id.record_autio);  
  42.         mPlayButton = (Button) findViewById(R.id.play_autio);  
  43.         mSysTime_TV = (TextView) findViewById(R.id.system_time);  
  44.   
  45.   
  46.         mRecordButton.setOnClickListener(this);  
  47.         mPlayButton.setOnClickListener(this);  
  48.     }  
  49.   
  50.     @Override  
  51.     public void onClick(View v) {  
  52.         switch (v.getId()) {  
  53.             case R.id.play_autio:  
  54.   
  55.                 onPlay(mStartPlaying);  
  56.                 if(mStartPlaying){  
  57.                     mPlayButton.setText("正在播放。。。");  
  58.                 }else{  
  59.                     mPlayButton.setText("点击播放");  
  60.                 }  
  61.                 mStartPlaying=!mStartPlaying;  
  62.                 break;  
  63.             case R.id.record_autio:  
  64.   
  65.                 onRecord(mStartRecording);  
  66.                 if(mStartRecording){  
  67.                     mRecordButton.setText("正在录音。。。");  
  68.                 }else{  
  69.                     mRecordButton.setText("点击录音");  
  70.                 }  
  71.                 mStartRecording=!mStartRecording;  
  72.   
  73.                 break;  
  74.         }  
  75.     }  
  76.   
  77.     private void onRecord(boolean start) {  
  78.             if(start){  
  79.                 startRecording();  
  80.             }else{  
  81.                 stopRecording();  
  82.             }  
  83.     }  
  84.   
  85.     private void onPlay(boolean start) {  
  86.         if(start){  
  87.             startPlaying();  
  88.         }else{  
  89.             stopPlaying();  
  90.         }  
  91.     }  
  92.   
  93.     private void stopPlaying() {  
  94.         mPlayer.release();  
  95.         mPlayer = null;  
  96.     }  
  97.   
  98.     private void startPlaying() {  
  99.         mPlayer = new MediaPlayer();  
  100.         try {  
  101.             mPlayer.setDataSource(mSavePath);  
  102.             mPlayer.prepare();  
  103.             mPlayer.start();  
  104.         } catch (IOException e) {  
  105.             Log.e(LOG_TAG, "prepare() failed");  
  106.         }  
  107.     }  
  108.   
  109.   
  110.     private void startRecording() {  
  111.         mMediaRecorder = new MediaRecorder();  
  112.         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);  
  113.         mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);  
  114.         mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);  
  115.         mMediaRecorder.setOutputFile(mSavePath);  
  116.         try {  
  117.             mMediaRecorder.prepare();  
  118.         } catch (IOException e) {  
  119.             Log.e(LOG_TAG, "prepare() failed");  
  120.             e.printStackTrace();  
  121.         }  
  122.         mMediaRecorder.start();  
  123.     }  
  124.   
  125.     private void stopRecording() {  
  126.         mMediaRecorder.stop();  
  127.         mMediaRecorder.release();  
  128.         mMediaRecorder = null;  
  129.         mSysTime_TV.setText("保存的文件:"+mSavePath);  
  130.     }  
  131.   
  132.     @Override  
  133.     protected void onPause() {  
  134.         super.onPause();  
  135.         if (mMediaRecorder != null) {  
  136.             mMediaRecorder.release();  
  137.             mMediaRecorder = null;  
  138.         }  
  139.   
  140.         if (mPlayer != null) {  
  141.             mPlayer.release();  
  142.             mPlayer = null;  
  143.         }  
  144.     }  
  145. }  

测试效果

simple demo

封装API

getMaxAmplitude()方法介绍

MediaRecorder里面有提供的getMaxAmplitude ()方法,该方法返回上次调用setMaxAmplitude()采集到的最大振幅,一个int整数,如下图:
003-getMaxAmplitude使用说明

获取振幅的强弱,可以设置类似微信语音的效果,根据说话声音振幅强弱的动画显示图标,测试效果
004-getMaxAmplitude使用说明

需要注意的,指定保存录音文件时,如果当前路径下的文件夹不存在,首先执行文件夹的创建,然后再保存录音文件,保存未指定setOutputFile(mSavePath)中的mSavePath,无法录音。

MediaUtil封装类工具

封装好需要调用的MediaRecorder方法,比如共同的代码封装到start()方法中,停止录音封装到stop()方法中,获取振幅大小封装到getAmplitude()方法,采用单例的设计模式,代码如下:

  1. package cn.teachcourse.api;  
  2.   
  3. import java.io.File;  
  4. import java.io.IOException;  
  5.   
  6. import android.os.Environment;  
  7. import android.media.MediaRecorder;  
  8.   
  9. /** 
  10.  * Created by postmaster@teachcourse.cn on 2016/3/1. 
  11.  */  
  12. public class MediaUtilAPI {  
  13.     private transient static MediaUtilAPI instance = null;  
  14.   
  15.     public static synchronized MediaUtilAPI getInstance() {  
  16.         if (instance == null) {  
  17.             instance = new MediaUtilAPI();  
  18.         }  
  19.         return instance;  
  20.     }  
  21.   
  22.     private File file;  
  23.     private MediaRecorder mRecorder = null;  
  24.   
  25.     public void start(String name) {  
  26.         if (!Environment.getExternalStorageState().equals(  
  27.                 android.os.Environment.MEDIA_MOUNTED)) {  
  28.             return;  
  29.         }  
  30.         file = new File(android.os.Environment.getExternalStorageDirectory()  
  31.                 + "/" + name);  
  32.         if (mRecorder == null) {  
  33.             mRecorder = new MediaRecorder();  
  34.             mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);  
  35.             mRecorder.setAudioSamplingRate(8000);  
  36.             mRecorder.setAudioEncodingBitRate(16);  
  37.             mRecorder.setAudioChannels(1);  
  38.             mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);  
  39.             mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);  
  40.             mRecorder.setOutputFile(file.getAbsolutePath());  
  41.             try {  
  42.                 mRecorder.prepare();  
  43.                 mRecorder.start();  
  44.             } catch (IllegalStateException e) {  
  45.                 System.out.print(e.getMessage());  
  46.             } catch (IOException e) {  
  47.                 System.out.print(e.getMessage());  
  48.             }  
  49.   
  50.         }  
  51.     }  
  52.   
  53.     public void stop() {  
  54.         if (mRecorder != null) {  
  55.             try {  
  56.                 mRecorder.setOnErrorListener(null);  
  57.                 mRecorder.stop();  
  58.                 mRecorder.release();  
  59.                 mRecorder = null;  
  60.             } catch (RuntimeException e) {  
  61.                 e.printStackTrace();  
  62.             }  
  63.   
  64.         }  
  65.     }  
  66.   
  67.     public int getAmplitude() {  
  68.         if (mRecorder != null)  
  69.             return mRecorder.getMaxAmplitude();  
  70.         else  
  71.             return 0;  
  72.   
  73.     }  
  74.   
  75. }  

类工具使用介绍

按钮添加一个长按事件,在按钮的状态显示录音强弱的图标,按钮松开状态录音结束并且图标消失,如下代码:

  1. @Override  
  2.     public boolean onTouch(View v, MotionEvent event) {  
  3.         switch (event.getAction()) {  
  4.             case MotionEvent.ACTION_DOWN:  
  5.   
  6.                 saveRecordFileNameLong= SystemClock.currentThreadTimeMillis();  
  7.   
  8.                 mMediaRecorder.start(saveRecordFileNameLong + ".amr");  
  9.                 mAudioViewState.setVisibility(View.VISIBLE);  
  10.   
  11.                 getAmplitude();  
  12.                 Log.e(LOG_TAG, "长按按钮:------------------->");  
  13.                 break;  
  14.             case MotionEvent.ACTION_UP:  
  15.                 mMediaRecorder.stop();  
  16.                 mAudioViewState.setVisibility(View.GONE);  
  17.                 break;  
  18.         }  
  19.         return false;  
  20.     }  
  21.   
  22.     Handler mHandler = new Handler() {  
  23.         @Override  
  24.         public void handleMessage(Message msg) {  
  25.             super.handleMessage(msg);  
  26.   
  27.         }  
  28.     };  
  29.   
  30.   
  31.     private void getAmplitude() {  
  32.             mHandler.postDelayed(new Runnable() {  
  33.                 @Override  
  34.                 public void run() {  
  35.   
  36.                     int mAmplitudeValue = mMediaRecorder.getAmplitude();  
  37.                     updateViewByAmplitudeValue(mAmplitudeValue);  
  38.   
  39.                     mHandler.postDelayed(this300);  
  40.                 }  
  41.             }, 300);  
  42.   
  43.     }  

MediaRecorder录制音频的MediaUtilApi工具封装完成,想要实现类似微信录音、QQ语音、易信语音或人人语音的功能,修改updateViewByAmplitudeValue中的代码、同时修改布局文件,感觉也不是特么困难的事情吧!!

关注公众号 扫一扫二维码,加我QQ

如果文章对你有帮助,欢迎点击上方按钮关注作者

来源:TeachCourse每周一次,深入学习Android教程,关注(QQ1589359239或公众号TeachCourse)
转载请注明出处:http://teachcourse.cn/1514.html

资源分享

录音源码下载
Android Studio如何使用桌面版GitHub管理项目? Android Studio如何使用桌面版
浅谈Layout类 浅谈Layout类
Android常用基本控件属性总结 Android常用基本控件属性总结
Android开发之HorizontalScrollView控件使用案例介绍 Android开发之HorizontalScro

发表评论

呲牙 憨笑 坏笑 偷笑 色 微笑 抓狂 睡觉 酷 流汗 鼓掌 大哭 可怜 疑问 晕 惊讶 得意 尴尬 发怒 奋斗 衰 骷髅 啤酒 吃饭 礼物 强 弱 握手 OK NO 勾引 拳头 差劲 爱你

表情