Android 5.0+ 屏幕錄制實現(xiàn)

前言

Android 從 4.0 開始就提供了手機錄屏方法,但是需要 root 權(quán)限,比較麻煩不容易實現(xiàn)。但是從 5.0 開始,系統(tǒng)提供給了 app 錄制屏幕的一系列方法,不需要 root 權(quán)限,只需要用戶授權(quán)即可錄屏,相對來說較為簡單。本文是在參考了網(wǎng)絡(luò)上其他錄屏資料后完成的,感謝。以下將介紹開發(fā)錄屏功能的一系列步驟以及實現(xiàn)過程中所遇到的一些需要注意的事項。

實現(xiàn)步驟

  1. 在清單文件中聲明需要的權(quán)限

因為錄制用到麥克風(fēng),所以需要加上 AUDIO 權(quán)限,

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

如果開發(fā)的 app targetApi 在 6.0 以上時,還需要動態(tài)獲取權(quán)限。

public static void checkPermission(AppCompatActivity activity) {
        if (Build.VERSION.SDK_INT >= 23) {
            int checkPermission =
                    ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO)
                            + ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_PHONE_STATE)
                            + ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                            + ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE);
            if (checkPermission != PackageManager.PERMISSION_GRANTED) {
                //動態(tài)申請
                ActivityCompat.requestPermissions(activity, new String[]{
                        Manifest.permission.RECORD_AUDIO,
                        Manifest.permission.READ_PHONE_STATE,
                        Manifest.permission.READ_EXTERNAL_STORAGE,
                        Manifest.permission.WRITE_EXTERNAL_STORAGE}, 123);
                return;
            } else {
                return;
            }
        }
        return;
    }
    
  1. 獲取用戶錄制屏幕授權(quán)

這里先介紹 MediaProjectionManager , MediaProjectionManager 是系統(tǒng)提供的一種服務(wù),當(dāng)我們拿到這個服務(wù)對象,可以創(chuàng)建一個 Intent ,通過這個 Intent 可以啟動一個彈框樣式的 Activity,如果用戶授權(quán)了,那我們便可以繼續(xù)下一步屏幕錄制。需要說明的是,Intent 是去啟動另一個 Activity 的,有極少極少的機型是沒有對應(yīng)的授權(quán)Activity 的,所以這里需要多加判斷,防止應(yīng)用奔潰。

MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) activity.
                            getSystemService(Context.MEDIA_PROJECTION_SERVICE);
                    if (mediaProjectionManager != null){
                        Intent intent = mediaProjectionManager.createScreenCaptureIntent();
                        PackageManager packageManager = activity.getPackageManager();
                        if (packageManager.resolveActivity(intent,PackageManager.MATCH_DEFAULT_ONLY) != null){
                            //存在錄屏授權(quán)的Activity 
                            activity.startActivityForResult(intent,requestCode);
                        }else {
                            Toast.makeText(activity,R.string.can_not_record_tip,Toast.LENGTH_SHORT).show();
                        }
                    }

  1. 在 onActivityResult 對用戶的授權(quán)做處理

即使用戶授權(quán)了,同意錄制操作,仍然需要捕獲異常,因為有可能會出現(xiàn)這樣一種情況,就是用戶在同意錄屏的時候系統(tǒng)也正在錄屏,錄屏操作沖突了。

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK){
            try {
                ScreenUtil.setUpData(resultCode,data);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            ToastUtil.show(this,"拒絕錄屏");
        }
    }
  1. 初始化 MediaRecorder、創(chuàng)建 VirtualDisplay
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void setUpMediaRecorder() {

        mRecordFilePath = getSaveDirectory()  + File.separator+  System.currentTimeMillis() + ".mp4";
        if (mMediaRecorder == null){
            mMediaRecorder = new MediaRecorder();
        }
        //設(shè)置音頻來源
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        //設(shè)置視頻來源
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
        //輸出的錄屏文件格式
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        //錄屏文件路徑
        mMediaRecorder.setOutputFile( mRecordFilePath );
        //視頻尺寸
        mMediaRecorder.setVideoSize(mRecordWidth, mRecordHeight);
        //音視頻編碼器
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
        //比特率
        mMediaRecorder.setVideoEncodingBitRate((int) (mRecordWidth * mRecordHeight * 3.6));
        //視頻幀率
        mMediaRecorder.setVideoFrameRate(20);

        try {
            mMediaRecorder.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    

這一步是整個錄屏操作最為關(guān)鍵的一步!我們初始化了 MediaRecorder,設(shè)置了是否錄上聲音、錄屏文件格式、錄屏文件路徑、音視頻的編碼器、比特率、視頻幀率等
然后將在步驟 3 中的 resultCode 以及 data 作為必要的參數(shù)通過 MediaProjectionManager 創(chuàng)建 VirtualDisplay。VirtualDisplay 可以理解為虛擬的呈現(xiàn)器,它可以捕獲屏幕上的內(nèi)容,并將其捕獲的內(nèi)容渲染到 Surface 上(Surace 由 MediaRecorder 提供,通過 getSurface() 方法得到),MediaRecorder 再進(jìn)一步將其封裝處理為 Mp4 文件。

經(jīng)過以上步驟 prepare 之后,當(dāng)再次調(diào)用 MediaRecorder.start() 就可以開始錄屏了,這里同意也需要注意的時,調(diào)用 start() 方法開始錄屏之后,不能立即調(diào)用 stop()方法停止錄屏,否則會奔潰。測試在測試錄屏功能時立馬停止錄屏,應(yīng)用奔潰,一直找不到原因。直到看到了源碼( API 26 )里的注釋

image

所以為了反正奔潰,在 stop 的時候捕獲異常,并且置空 MediaRecorder,下次錄屏的時候再重新生成 MediaRecorder。

最后

由于知識水平有限,難免有錯誤遺漏,歡迎指正!項目地址為 屏幕錄制

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,045評論 25 709
  • 這樣說不知道合不合適,首先我認(rèn)為兩個人在一起總要有一個能容忍對方,這樣才不會有過多的矛盾產(chǎn)生。 但是這樣另一個人承...
    囖囖囖閱讀 258評論 0 0
  • 分享人:易仁永澄目的:說明元規(guī)范為什么有用;原理是什么;具體結(jié)構(gòu)是什么 一、正文 【問題】在沒有看過元規(guī)范以及看過...
    右手戒指閱讀 303評論 0 0
  • 歡迎來到小于的王者地盤,小于已經(jīng)更新了王者峽谷71位英雄全新的推薦出裝以及銘文搭配(新英雄百里玄策的教學(xué)已經(jīng) 上線...
    王者小于幫閱讀 387評論 0 2

友情鏈接更多精彩內(nèi)容