最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android錄音功能的實(shí)現(xiàn)以及踩坑實(shí)戰(zhàn)記錄

 更新時(shí)間:2022年06月01日 09:48:28   作者:左大星  
在Android 開發(fā)過程中,有些功能是通用的,或者是多個(gè)業(yè)務(wù)方都需要使用的,下面這篇文章主要給大家介紹了關(guān)于Android錄音功能的實(shí)現(xiàn)以及踩坑的相關(guān)資料,需要的朋友可以參考下

前言

最近接到個(gè)需求,不使用第三方SDK的情況下實(shí)現(xiàn)IM通訊,文字聊天已經(jīng)通過MQTT實(shí)現(xiàn),而語音功能目前想到的較好解決方案就是進(jìn)行錄音文件的上傳下載??赡苓€有更好解決方案,但我目前沒想到,有建議的小伙伴勞煩指導(dǎo)下。

前提

1、權(quán)限申請: 清單文件中加上:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />

對應(yīng)讀寫文件和錄音權(quán)限。

2、錄音文件要寫到相應(yīng)文件夾中,此文件夾要先創(chuàng)建,丟到Application或Activity的onCreate()中都可以,但一定要先創(chuàng)建。

代碼實(shí)現(xiàn)流程

1、創(chuàng)建MediaRecorder對象;

2、調(diào)用setAudioSource()方法設(shè)置聲音的來源,一般傳入MediaRecorder.MIC;

3、調(diào)用setOutputFormat()設(shè)置所錄制的音頻文件的格式;

4、調(diào)用setAudioRncoder()、setAudioEncodingBitRate(int bitRate)、setAudioSamlingRate(int SamplingRate)設(shè)置所錄音的編碼格式、編碼位率、采樣率等,當(dāng)然不是每個(gè)都需要,根據(jù)具體業(yè)務(wù)調(diào)整(setAudioEncodingBitRate(96000),編碼位率一般是96000);

5、調(diào)用setOutputFile(String path)方法設(shè)置錄制的音頻文件的保存位置;

6、調(diào)用MediaRecoder對象的Prepare()方法準(zhǔn)備錄制;

7、調(diào)用MediaRecoder對象的start()方法開始錄制;

8、結(jié)束后調(diào)用MediaRecoder對象的stop()方法停止錄制,并調(diào)用release()方法釋放資源。 示例如下:

public class TestActivity extends BaseActivity {
    private ActivityChatBinding testBinding;
    private MediaRecorder mediaRecorder;
    private boolean isRecorded;

    @Override
    public void initView() {
        testBinding = ActivityTestBinding.inflate(getLayoutInflater());
        setContentView(testBinding.getRoot());
        initMsgAndSth();
        checkPermission();
    }

    private void initMsgAndSth(){
        String record_Home = this.getFilesDir()+"/Sample";  //聲明存儲路徑,用絕對路徑什么都可以
        //btnTalk就是個(gè)Button
        testBinding.btnTalk.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isRecorded)
                    stopRecordAudio();
                else
                    startRecordAudio(record_Home);
            }
        });
    }

    private void stopRecordAudio() {
        //有的5.0機(jī)型上MediaRecorder.stop會報(bào)錯(cuò),這里建議抓取一下異常
        if(mediaRecorder !=null){
            try {
                mediaRecorder.stop();//停止錄音
                mediaRecorder.release();//釋放資源
                mediaRecorder =null;
            }catch (Exception exception){
                mediaRecorder.reset();//重置
                mediaRecorder.release();//釋放資源
                mediaRecorder =null;
            }
            Toast.makeText(this,"停止錄音",Toast.LENGTH_SHORT).show();
        }
    }

    private void startRecordAudio(String path) {
        //文件夾一定要先創(chuàng)建,不然報(bào)錯(cuò)的bug信息中是找不到這里的
        File audioFile = new File(path);
        if (!audioFile.exists()) {
            audioFile.mkdirs();
        } else if (!audioFile.isDirectory()) {
            audioFile.delete();
            audioFile.mkdirs();
        }
        File file = new File(path + "Sample.amr");
        if(!file.exists()){
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(mediaRecorder == null){
            mediaRecorder = new MediaRecorder();
            mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//設(shè)置麥克風(fēng)
            /*
             * 設(shè)置保存輸出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default THREE_GPP(3gp格式
             * ,H263視頻/ARM音頻編碼)、MPEG-4、RAW_AMR(只支持音頻且音頻編碼要求為AMR_NB)
             */
            mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);

            mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);//設(shè)置音頻文件編碼格式
            mediaRecorder.setOutputFile(path+"Sample.amr");
        }
        try {
            mediaRecorder.prepare();  //start之前要先prepare
            mediaRecorder.start();
            isRecorded = true;
            Toast.makeText(this,"開始錄音",Toast.LENGTH_SHORT).show();
        } catch (IllegalStateException el){
            el.printStackTrace();
        } catch (RuntimeException e){
            e.printStackTrace();
        } catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 簡單的權(quán)限申請邏輯
     */
    private void checkPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            String[] permissions = new String[]{Manifest.permission.RECORD_AUDIO,Manifest.permission.WRITE_EXTERNAL_STORAGE};
            for (String permission : permissions) {
                if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
                    ActivityCompat.requestPermissions(this, permissions, 200);
                    return;
                }
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[]
            grantResults) {
        super.onRequestPermissionsResult(requestCode,permissions,grantResults);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && requestCode == 200) {
            for (int i = 0; i < permissions.length; i++) {
                if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                    Intent intent = new Intent();
                    intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                    Uri uri = Uri.fromParts("package", getPackageName(), null);
                    intent.setData(uri);
                    startActivityForResult(intent, 200);
                    return;
                }
            }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK && requestCode == 200) {
            checkPermission();
        }
    }
}

相關(guān)API差異已經(jīng)寫的很詳細(xì)了,布局很簡單,這里就不貼出來了。這里要特別注意的是調(diào)用順序不能改變,否則容易報(bào)錯(cuò),且因?yàn)檎{(diào)用順序不對而報(bào)錯(cuò)的提示信息也不一定足夠去定位問題。

踩坑

按照前面的提示,覺得避開所有坑能愉快的玩耍了,結(jié)果運(yùn)行報(bào)錯(cuò),有的機(jī)型還不打印特定日志,只能自己去鼓搗。

1、Android Q:

有的時(shí)候根據(jù)報(bào)錯(cuò)分類,還是能抓到點(diǎn)蛛絲馬跡。如果是Android Q的設(shè)備,報(bào)IO異?;蛘逷ermission Denied錯(cuò)誤,則要檢查下清單文件中application標(biāo)簽里有沒有這句:

android:requestLegacyExternalStorage="true"

沒有的話一定要加上。

原因在于安卓10開始,要想訪問外部存儲的所有文件,除了動(dòng)態(tài)申請權(quán)限權(quán)限申明外,必須在主工程AndroidManifest.xml中加上這句,用于申請外部存儲所有文件的權(quán)限。

2、RuntimeException:setAudioSource failed

如果程序運(yùn)行看到

RuntimeException: setAudioSource failed

報(bào)錯(cuò),請確保申請權(quán)限相關(guān)邏輯正確,還有清單文件中相關(guān)權(quán)限的申請,但如果(雖然是極少概率,但我碰到了)添加權(quán)限后,依舊還報(bào)這個(gè)錯(cuò),請進(jìn)入手機(jī)設(shè)置-應(yīng)用,找到你發(fā)布上去的應(yīng)用,給其授權(quán)。部分機(jī)型在調(diào)試過程中除了第一次會提示授權(quán)外,再次安裝則不會再提示,這就相當(dāng)于用戶沒有授予相關(guān)的錄音和sdcard讀寫權(quán)限,程序依然會報(bào)錯(cuò)。所以,建議每次開始進(jìn)行錄音等邏輯前,進(jìn)行一次邏輯判斷。

此外還有一種情況會出現(xiàn)此報(bào)錯(cuò),在錄音結(jié)束后沒有調(diào)用mediaRecorder.release()去釋放資源,而又處于stop狀態(tài),這時(shí)候再去prepare、start容易報(bào)此錯(cuò),此時(shí)報(bào)錯(cuò)打印堆棧與原先堆棧報(bào)錯(cuò)信息差別不大,較難定位,因此要格外注意。

3、MediaRecorder: stop failed

在調(diào)用start()后,馬上進(jìn)行調(diào)用stop()的操作,由于沒有生成有效的音頻或是視頻數(shù)據(jù),會報(bào)此錯(cuò)誤。這個(gè)情景在即時(shí)通訊過程中很常見,可以通過讓其線程睡眠小段時(shí)間(建議最少1秒),再stop()。官方文檔注釋對此也有解釋:Note that a RuntimeException is intentionally thrown to the application, if no valid audio/video data has been received when stop() is called.

總結(jié)

到此這篇關(guān)于Android錄音功能的實(shí)現(xiàn)以及踩坑的文章就介紹到這了,更多相關(guān)Android錄音功能踩坑內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

镇沅| 正安县| 炉霍县| 宜春市| 望都县| 蒲江县| 新干县| 石城县| 邵武市| 衡水市| 弋阳县| 金坛市| 台州市| 东乡| 农安县| 长春市| 朝阳市| 镶黄旗| 尉犁县| 渭南市| 吉隆县| 灵宝市| 车致| 平山县| 林芝县| 梅河口市| 江都市| 蒙自县| 麦盖提县| 晋城| 南江县| 辽源市| 阳信县| 徐闻县| 乌拉特后旗| 山东| 米易县| 丰都县| 阆中市| 石楼县| 天全县|