Android 5.0+ 螢幕錄製實現

Umsdd發表於2018-05-16

前言

Android 從 4.0 開始就提供了手機錄屏方法,但是需要 root 許可權,比較麻煩不容易實現。但是從 5.0 開始,系統提供給了 app 錄製螢幕的一系列方法,不需要 root 許可權,只需要使用者授權即可錄屏,相對來說較為簡單。本文是在參考了網路上其他錄屏資料後完成的,感謝。以下將介紹開發錄屏功能的一系列步驟以及實現過程中所遇到的一些需要注意的事項。

實現步驟

  1. 在清單檔案中宣告需要的許可權

因為錄製用到麥克風,所以需要加上 AUDIO 許可權,

<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"/>
複製程式碼

如果開發的 app targetApi 在 6.0 以上時,還需要動態獲取許可權。

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) {
                //動態申請
                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. 獲取使用者錄製螢幕授權

這裡先介紹 MediaProjectionManager , MediaProjectionManager 是系統提供的一種服務,當我們拿到這個服務物件,可以建立一個 Intent ,通過這個 Intent 可以啟動一個彈框樣式的 Activity,如果使用者授權了,那我們便可以繼續下一步螢幕錄製。需要說明的是,Intent 是去啟動另一個 Activity 的,有極少極少的機型是沒有對應的授權Activity 的,所以這裡需要多加判斷,防止應用奔潰。

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){
                            //存在錄屏授權的Activity 
                            activity.startActivityForResult(intent,requestCode);
                        }else {
                            Toast.makeText(activity,R.string.can_not_record_tip,Toast.LENGTH_SHORT).show();
                        }
                    }

複製程式碼
  1. 在 onActivityResult 對使用者的授權做處理

即使使用者授權了,同意錄製操作,仍然需要捕獲異常,因為有可能會出現這樣一種情況,就是使用者在同意錄屏的時候系統也正在錄屏,錄屏操作衝突了。

@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、建立 VirtualDisplay
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void setUpMediaRecorder() {

        mRecordFilePath = getSaveDirectory()  + File.separator+  System.currentTimeMillis() + ".mp4";
        if (mMediaRecorder == null){
            mMediaRecorder = new MediaRecorder();
        }
        //設定音訊來源
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        //設定視訊來源
        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();
        }
    }
    
複製程式碼

這一步是整個錄屏操作最為關鍵的一步!我們初始化了 MediaRecorder,設定了是否錄上聲音、錄屏檔案格式、錄屏檔案路徑、音視訊的編碼器、位元率、視訊幀率等 然後將在步驟 3 中的 resultCode 以及 data 作為必要的引數通過 MediaProjectionManager 建立 VirtualDisplay。VirtualDisplay 可以理解為虛擬的呈現器,它可以捕獲螢幕上的內容,並將其捕獲的內容渲染到 Surface 上(Surace 由 MediaRecorder 提供,通過 getSurface() 方法得到),MediaRecorder 再進一步將其封裝處理為 Mp4 檔案。

經過以上步驟 prepare 之後,當再次呼叫 MediaRecorder.start() 就可以開始錄屏了,這裡同意也需要注意的時,呼叫 start() 方法開始錄屏之後,不能立即呼叫 stop()方法停止錄屏,否則會奔潰。測試在測試錄屏功能時立馬停止錄屏,應用奔潰,一直找不到原因。直到看到了原始碼( API 26 )裡的註釋

Android 5.0+ 螢幕錄製實現
所以為了反正奔潰,在 stop 的時候捕獲異常,並且置空 MediaRecorder,下次錄屏的時候再重新生成 MediaRecorder。

最後

由於知識水平有限,難免有錯誤遺漏,歡迎指正!專案地址為 螢幕錄製

相關文章