目录
本章教大家实现后台录制手机屏幕和定时截屏的功能。
可以录制App外的屏幕,不限于本App内部喔。
禁止转载,侵权必究!Update2022.05.14
创建plugin项目
命令行执行以下命令:
flutter create --org cn.abilitygame -t plugin -i objc -a java
--platforms android,ios ability_media_projection_plugin
build一下项目:
cd ability_media_projection_plugin/example
flutter build apk
打开Android Studio,选择import project,一路选到项目中的example的android目录下,点击Open。
编写Android部分代码
增加主类接口,以实现录屏功能:
public class AbilityMediaProjectionPlugin implements FlutterPlugin, ActivityAware, MethodCallHandler, PluginRegistry.ActivityResultListener {}
先看ActivityAware接口的关键方法实现(要把绑定的activity存下来):
@Override
public void onAttachedToActivity(ActivityPluginBinding binding) {
this.activity = binding.getActivity();
...
}
再看PluginRegistry.ActivityResultListener接口的关键方法实现,要实现onActivityResult方法。因为录屏需要启动一个新的Intent,这个时候需要绑定一个新的Activity并监听它的回调。
@Override
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
if( requestCode == SCREEN_RECORD_REQUEST_CODE ) {
if( resultCode == Activity.RESULT_OK ){
...
_result.success("start media recorder successfully!");
return true;
}else {
_result.error("500", "Activity callback:" + resultCode, "");
}
}else{}
return false;
}
在onMethodCall方法中实现启动录屏的代码:
if (call.method.equals("startRecordScreen")) {
...
Intent permissionIntent = mediaProjectionManager.createScreenCaptureIntent();
ActivityCompat.startActivityForResult(activity, permissionIntent, SCREEN_RECORD_REQUEST_CODE, null);
}
按Android规范创建了录屏的Intent并启动一个新的Activity来执行录屏。回到onActivityResult回调方法的实现,我们首先要初始化MediaRecorder,然后关联到MediaProjection投射类,投射到一个VirtualDisplay,然后再启动MediaRecorder。
if( resultCode == Activity.RESULT_OK ){
// 初始化
initMediaRecorder();
mediaProjection = mediaProjectionManager.getMediaProjection(activity.RESULT_OK,data);
if (mediaProjection == null){
_result.error("500","mediaProjection is null","");
}else{}
activityCallBack = new ActivityCallBack();
mediaProjection.registerCallback(activityCallBack, null);
// start前先把virtualDisplay创建好
surface = mediaRecorder.getSurface();
// 创建虚拟显示器
virtualDisplay = mediaProjection.createVirtualDisplay("MainActivity",
DISPLAY_WIDTH, DISPLAY_HEIGHT, DPI, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
surface, null, null);
mediaRecorder.start();
_result.success("start media recorder successfully!");
return true;
}
我们再看下initMediaRecorder方法:
private void initMediaRecorder() {
try {
mediaRecorder = new MediaRecorder();
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mediaRecorder.setOutputFile( storePath + "test.mp4" );
mediaRecorder.setVideoSize(DISPLAY_WIDTH, DISPLAY_HEIGHT);
mediaRecorder.setVideoFrameRate(60);
// mediaRecorder.setOrientationHint(90);
mediaRecorder.setVideoEncodingBitRate(5*DISPLAY_HEIGHT*DISPLAY_WIDTH);
mediaRecorder.prepare();
} catch (Exception e) {
Log.i( TAG, e.getMessage() );
}
}
setVideoEncodingBitRate是提升清晰度的,其他的参数应该非常好理解。
注意:完整的代码实现请看文章最后的源码url。
编写Dart部分
关闭Android Studio。重新打开Android Studio,选择Open an exist Android Studio Project。
定义私有变量,存储录屏操作各个步骤的返回结果:
String _opResult = "";
实现两个方法,分别对应开始录屏和结束录屏:
void startRecord() {
AbilityMediaProjectionPlugin.startRecord.then((value) => setState((){
_opResult = value;
}));
}
void stopRecord() {
AbilityMediaProjectionPlugin.stopRecord.then((value) => setState((){
_opResult = value;
}));
}
定义UI界面
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('录屏测试'),
),
body: Column(
children: <Widget>[
Text("录屏能力验证:"),
RaisedButton(
onPressed: () => startRecord(),
color: Colors.lightBlueAccent,
child: Text('开始录屏', style: TextStyle(fontSize: 10)),
),
RaisedButton(
onPressed: () => stopRecord(),
color: Colors.lightBlueAccent,
child: Text('结束录屏', style: TextStyle(fontSize: 10)),
),
Text('$_opResult'),
],
),
),
);
}
定义MethodChannel交互类
class AbilityMediaProjectionPlugin {
static const MethodChannel _channel =
const MethodChannel('ability_media_projection_plugin');
static Future<String> get startRecord async {
final String version = await _channel.invokeMethod('startRecordScreen');
return version;
}
static Future<String> get stopRecord async {
final String version = await _channel.invokeMethod('stopRecordScreen');
return version;
}
}
测试自定义插件
需要点击2次开始录屏,因为在代码实现中第一次点击会获取基础权限(比如写入磁盘),第二次点击才是申请录屏权限并开始录屏。点击结束录屏后,我们可以在相册–>其他相册–>DCIM 下找到录屏的mp4文件。
机型适配问题
适用于所有Android5.0以上的机型(基本上包含了所有市面上的机型)