Android 录音中的那些坑
来源:互联网 发布:linux网络设置 编辑:程序博客网 时间:2024/05/22 08:27
每一个看似简单的功能背后,都是攻城狮们用汗水填起的坑,尤其在这么热的天。
最近又邂逅了Android的录音模块,由于是前同事留下的代码,bug改得宝宝真是欲仙欲死。首先看下原始的实现:
其中RecorderHelp中封装了MediaRecorder,录音这种耗时操作放在了AsyncTask中进行。其实并说不上这种设计有什么缺点,但在使用的过程确实虐心,下边是自己的实现。
一个比较恶心的问题,如何在开启录音前增加权限检测。在Google原生SDK上,当然可以使用CompatContext.checkSelfPermission来做,但碰到小米华为等手机,这个方法就gg了,无论用户是否禁止了录音权限,都会返回给PERMISSION_GRANTED。没办法,果断google,大概有如下几个方案:
1.try catch MediaRecorder在start的时候,如果没有权限会抛出状态异常,但尝试后发现华为手机是不抛任何异常的,小米会启动失败。
2.预录制一段音频,判断麦克风振幅。但这个方案在小米上貌似是可以的,他妈的华为的又gg了。
3.接着2,华为的虽然靠振幅不能判断,但P7上是不会生成录音文件的,大喜。结果提交后,QA反馈honor上在权限被禁的情况下可以录制1s的文件。。。。要去日几只狗。
后边老大提醒看下微信是如何做的,看了下微信,体验还真不错,但突然想到微信是可以语音转文字的啊,那肯定是用AudioRecorder了。随手Google下AudioRecorder,有哥们给出了如下方案
最近又邂逅了Android的录音模块,由于是前同事留下的代码,bug改得宝宝真是欲仙欲死。首先看下原始的实现:
其中RecorderHelp中封装了MediaRecorder,录音这种耗时操作放在了AsyncTask中进行。其实并说不上这种设计有什么缺点,但在使用的过程确实虐心,下边是自己的实现。
一个比较恶心的问题,如何在开启录音前增加权限检测。在Google原生SDK上,当然可以使用CompatContext.checkSelfPermission来做,但碰到小米华为等手机,这个方法就gg了,无论用户是否禁止了录音权限,都会返回给PERMISSION_GRANTED。没办法,果断google,大概有如下几个方案:
1.try catch MediaRecorder在start的时候,如果没有权限会抛出状态异常,但尝试后发现华为手机是不抛任何异常的,小米会启动失败。
2.预录制一段音频,判断麦克风振幅。但这个方案在小米上貌似是可以的,他妈的华为的又gg了。
3.接着2,华为的虽然靠振幅不能判断,但P7上是不会生成录音文件的,大喜。结果提交后,QA反馈honor上在权限被禁的情况下可以录制1s的文件。。。。要去日几只狗。
后边老大提醒看下微信是如何做的,看了下微信,体验还真不错,但突然想到微信是可以语音转文字的啊,那肯定是用AudioRecorder了。随手Google下AudioRecorder,有哥们给出了如下方案
public static int getRecordState() { int minBuffer = AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT); AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, 44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, (minBuffer * 100)); short[] point = new short[minBuffer]; int readSize = 0; try { audioRecord.startRecording();//检测是否可以进入初始化状态 } catch (Exception e) { if (audioRecord != null) { audioRecord.release(); audioRecord = null; LogUtils.d("CheckAudioPermission", "无法进入录音初始状态"); } return STATE_NO_PERMISSION; } if (audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) { //6.0以下机型都会返回此状态,故使用时需要判断bulid版本 //检测是否在录音中 if (audioRecord != null) { audioRecord.stop(); audioRecord.release(); audioRecord = null; LogUtils.d("CheckAudioPermission", "录音机被占用"); } return STATE_RECORDING; } else { //检测是否可以获取录音结果 readSize = audioRecord.read(point, 0, point.length); if (readSize <= 0) { if (audioRecord != null) { audioRecord.stop(); audioRecord.release(); audioRecord = null; } LogUtils.d("CheckAudioPermission", "录音的结果为空"); return STATE_NO_PERMISSION; } else { if (audioRecord != null) { audioRecord.stop(); audioRecord.release(); audioRecord = null; } return STATE_SUCCESS; } } }
这个在测试后发现可以兼容大部分的手机。收工。
---------------------------------------------------------------------------------------------------
上述代码发现在米3上仍不能使用,不管录音权限是否打开,米3返回的state始终为failed,该型号的手机至今没有找到解决方案,求大神们路过告知。
接下来是监听语音播放时耳机插入与拔出事件,不多说先上代码
package com.didapinche.booking.common.voice;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.media.AudioManager;import android.media.MediaPlayer;import android.net.Uri;import android.os.Build;import com.apkfuns.logutils.LogUtils;import com.didapinche.booking.app.CarpoolApplication;import java.io.File;import java.io.IOException;/** * Created by leeks on 2016/8/22. */public class VoicePlayer { /** * 外放模式 */ public static final int MODE_SPEAKER = 0; /** * 耳机模式 */ public static final int MODE_HEADSET = 1; /** * 听筒模式 */ public static final int MODE_EARPIECE = 2; private AudioManager audioManager; private MediaPlayer mediaPlayer; private PlayCallback callback; private Context context; private HeadsetReceiver receiver; private IntentFilter filter; private boolean isPause = false; private int currentMode = MODE_SPEAKER; private boolean isRegisterReceiver = false; private static class Singleton { public static VoicePlayer instance = new VoicePlayer(); } public static VoicePlayer getInstance() { return Singleton.instance; } private VoicePlayer() { this.context = CarpoolApplication.context; initMediaPlayer(); initAudioManager(); initReceiver(); } public void initReceiver() { receiver = new HeadsetReceiver(); filter = new IntentFilter(); filter.addAction(Intent.ACTION_HEADSET_PLUG); filter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY); } /** * 初始化播放器 */ private void initMediaPlayer() { mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); } /** * 初始化音频管理器 */ private void initAudioManager() { audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); } else { audioManager.setMode(AudioManager.MODE_IN_CALL); } audioManager.setSpeakerphoneOn(true); //默认为扬声器播放 } /** * 播放回调接口 */ public interface PlayCallback { /** * 音乐准备完毕 */ void onPrepared(); /** * 音乐播放完成 */ void onComplete(); /** * 音乐停止播放 */ void onStop(); } public void play(String path, final PlayCallback callback) { play(Uri.parse(path), callback); } public void play(File file, PlayCallback callback) { play(Uri.fromFile(file), callback); } private void play(Uri uri, final PlayCallback callback) { this.callback = callback; if (!isRegisterReceiver) { context.registerReceiver(receiver, filter); isRegisterReceiver = true; } if (mediaPlayer == null) { initMediaPlayer(); } try { mediaPlayer.reset(); mediaPlayer.setDataSource(context, uri); mediaPlayer.prepareAsync(); mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { if (callback != null) { callback.onPrepared(); } mediaPlayer.start(); } }); mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { if (callback != null) { callback.onComplete(); } resetPlayMode(); } }); } catch (IOException e) { e.printStackTrace(); } } public boolean isPause() { return isPause; } public void pause() { if (isPlaying()) { isPause = true; mediaPlayer.pause(); } } public void resume() { if (isPause) { isPause = false; mediaPlayer.start(); } } /** * 获取当前播放模式 * * @return */ public int getCurrentMode() { return currentMode; } /** * 切换到听筒模式 */ public void changeToEarpieceMode() { currentMode = MODE_EARPIECE; audioManager.setSpeakerphoneOn(false); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, audioManager.getStreamMaxVolume(AudioManager.MODE_IN_COMMUNICATION), AudioManager.FX_KEY_CLICK); } else { audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, audioManager.getStreamMaxVolume(AudioManager.MODE_IN_CALL), AudioManager.FX_KEY_CLICK); } } /** * 切换到耳机模式 */ public void changeToHeadsetMode() { currentMode = MODE_HEADSET; audioManager.setSpeakerphoneOn(false); } /** * 切换到外放模式 */ public void changeToSpeakerMode() { currentMode = MODE_SPEAKER; audioManager.setSpeakerphoneOn(true); } public void resetPlayMode() { if (audioManager.isWiredHeadsetOn()) { changeToHeadsetMode(); } else { changeToSpeakerMode(); } } /** * 调大音量 */ public void raiseVolume() { int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); if (currentVolume < audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)) { audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, AudioManager.FX_FOCUS_NAVIGATION_UP); } } /** * 调小音量 */ public void lowerVolume() { int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); if (currentVolume > 0) { audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, AudioManager.FX_FOCUS_NAVIGATION_UP); } } /** * 停止播放 */ public void stop() { if (isPlaying()) { try { mediaPlayer.stop(); callback.onStop(); } catch (IllegalStateException e) { e.printStackTrace(); } } } public void release() { stop(); try { if (mediaPlayer != null) { mediaPlayer.release(); mediaPlayer = null; } } finally { if (isRegisterReceiver) { try { context.unregisterReceiver(receiver); } catch (Exception e) { e.printStackTrace(); } isRegisterReceiver = false; } } } /** * 是否正在播放 * * @return 正在播放返回true, 否则返回false */ public boolean isPlaying() { return mediaPlayer != null && mediaPlayer.isPlaying(); } public int getDuration() { if (mediaPlayer != null) { return mediaPlayer.getDuration(); } return 0; } public class HeadsetReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); switch (action) { //插入和拔出耳机会触发此广播 case Intent.ACTION_HEADSET_PLUG: int state = intent.getIntExtra("state", 0); if (state == 1) { LogUtils.e("耳机已插入"); changeToHeadsetMode(); } else if (state == 0) { LogUtils.e("音乐恢复播放"); resume(); } break; //拔出耳机会触发此广播,拔出不会触发,且此广播比上一个早,故可在此暂停播放,收到上一个广播时在恢复播放 case AudioManager.ACTION_AUDIO_BECOMING_NOISY: pause(); LogUtils.e("耳机已拔出"); if (isPause()) { LogUtils.e("音乐已暂停"); } changeToSpeakerMode(); break; default: break; } } }}
0 0
- Android 录音中的那些坑
- Android中的录音效果
- Android中的那些坑!
- android 开发录音那些事(录音权限授权及判断录音权限是否拒绝处理)
- Android中的录音与音频合成
- 浅析android适配器adapter中的那些坑
- android webview 在项目中的那些坑
- Android|Java 开发中的那些坑 ConcurrentModificationException
- Android中的那些权限
- Android 中的那些权限
- Android 中的那些权限
- Android通话录音之坑
- Android 录音
- android 录音
- Android 录音
- android 录音
- android 录音
- android 录音
- 排序(2)-选择排序
- Android 悬浮窗权限各机型各系统适配大全
- 使用zxing生成带logo的二维码图片
- android Imageview 显示本地绝对路径图片
- org.json源码分析及增强(二)——org.json.JSONException: Duplicate key问题处理
- Android 录音中的那些坑
- 架构以及架构的本质
- 关于单机winform数据库 数据存储
- java序列化
- Appium+Robotframework实现手机应用的自动化测试-1:Appium简介
- Spring+SpringMVC+Mybatis+Mysql整合实例
- 素数对猜想
- 自己总结面试题
- Android中shape中的属性大全