Android 录音中的那些坑

每一个看似简单的功能背后,都是攻城狮们用汗水填起的坑,尤其在这么热的天。


最近又邂逅了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;
            }
        }
    }

}


你可能感兴趣的:(android开发)