开始之前先说明一下关键几个类的作用:
- MediaProjectionManager:录屏主要管理类,发出截屏意图提醒、创建录屏等
- IBinder:IBinder是Android中的一个接口,它定义了一组用于进程间通信的方法。通过IBinder,我们可以在不同的进程之间传递数据和调用方法,实现进程间的交互。在Android系统中,每个进程都有一个唯一的Binder对象,用于管理该进程中的Binder通信。
- ServiceConnection:用于管理应用程序和服务之间的连接
- MediaRecorder:Android sdk提供的一个专门用于音视频录制,一般利用手机麦克风采集音频,摄像头采集图片信息,设置参数等。
- VirtualDisplay:创建虚拟屏幕
1、注册service组件
录屏需要后台运行服务支持,需要在mannifest中注册service组件
<service android:name=".ScreenService"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="mediaProjection"/>
2、activity中通过Intent绑定服务
Intent intent = new Intent(this, ScreenService.class);
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
- 首先,绑定开始之后Android系统调用service的onBind()方法,它返回一个用来与service交互的IBinder对象,但是绑定是异步的,他需要通过mServiceConnection里面的方法来传递(后面讲)
//首先,客户端调用bindService绑定的时候会执行service中的onBind这个方法
@Override
public IBinder onBind(Intent intent) {
return new RecordBinder();
}
//service里面的内部类
public class RecordBinder extends Binder {
//里面的getRecordService方法,待会activity会调用他
public ScreenService getRecordService() {
return ScreenService.this;
}
}
-
这里干了一件事,绑定服务的时候返回一个Binder,这个binder中有一个getRecordService方法,待会客户端activity那边调用这个方法可以直接返回当前的service
-
刚才讲了绑定的时候传递了一个mServiceConnection对象,上面返回的IBinder对象会在这个类的方法中返回,拿到返回的这个IBinder对象我们就可以调用该对象的方法(这个方法是我们自己写的,返回的是当前的service对象,这样我们就在客户端的activity中拿到了后台运行的service对象)
//这个mServiceConnection对象就是绑定服务的时候传递进去的
private ServiceConnection mServiceConnection = new ServiceConnection() {
// 当与service的连接建立后被调用
@Override
public void onServiceConnected(ComponentName className, IBinder myBinder) {
//ScreenService.RecordBinder是service中的内部类,binder就是我们返回的IBinder对象
ScreenService.RecordBinder binder = (ScreenService.RecordBinder) myBinder;
//通过返回的这个IBinder对象去调用他里面的getRecordService方法,的到的时候service对象
ScreenService recordService = binder.getRecordService();
recordService.setConfig(metrics.widthPixels, metrics.heightPixels, metrics.densityDpi);
}
// 当与service的连接意外断开时被调用 注意是意外断开的时候调用)
@Override
public void onServiceDisconnected(ComponentName arg0) {
}
};
3. 录制屏幕弹窗提示申请
- 开始检查权限(存储权限、录音权限,权限不是本文的重点,略过)
- 权限都在的时候开始申请屏幕录制
//通过MediaProjectionManager这个类来启动,这个是录屏之前的一个保护弹窗,询问你是不是需要开始录屏
//点击同意之后系统会回调到onActivityResult 通过这个REQUEST_CODE_RECORD参数来标识
private MediaProjectionManager projectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
Intent captureIntent = projectionManager.createScreenCaptureIntent();
startActivityForResult(captureIntent, REQUEST_CODE_RECORD);
//点击同意之后如下
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
if (requestCode == REQUEST_CODE_RECORD)//上述传递的标识参数
{
//录屏逻辑
Intent service = new Intent(this, ScreenService.class);
service.putExtra("resultCode", resultCode);
service.putExtra("requestCode", requestCode);
service.putExtra("data", data);
//启动service服务,启动服务之后会执行service中的onStartCommand,可以在onStartCommand中做逻辑操作
startForegroundService(service);
// handler.postDelayed(runnable, 10*1000);//最多录制10s
}
}
}
4. 开始录制,启动服务之后执行service中的onStartCommand方法,然后开始录屏逻辑的实现
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
int resultCode = intent.getIntExtra("resultCode", -1);
int requestCode = intent.getIntExtra("requestCode", -1);
Intent resultData = intent.getParcelableExtra("data");
startNotification();
projectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
mediaProjection = projectionManager.getMediaProjection(resultCode, resultData);//必须在通知显示之后调用
//录屏
try {
startRecord();
} catch (JSONException e) {
e.printStackTrace();
}
return super.onStartCommand(intent, flags, startId);
}
private final String NOTIFICATION_CHANNEL_ID="com.yun.screenrecord.MediaService";
private final String NOTIFICATION_CHANNEL_NAME="com.yun.screenrecord.channel_name";
private final String NOTIFICATION_CHANNEL_DESC="com.yun.screenrecord.channel_desc";
public void startNotification() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//Call Start foreground with notification
Intent notificationIntent = new Intent(this, ScreenService.class);
PendingIntent pendingIntent;//适配31及以上的版本隐式启动
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
} else {
pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_ONE_SHOT);
}
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_foreground))
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("Starting recording")
.setContentText("Starting screen record service")
.setContentIntent(pendingIntent);
Notification notification = notificationBuilder.build();
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription(NOTIFICATION_CHANNEL_DESC);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
startForeground(1, notification); //必须使用此方法显示通知,不能使用notificationManager.notify,否则还是会报上面的错误
}
}
5. 配置录屏的相关操作,完善startRecord代码
public boolean startRecord() throws JSONException {
if (mediaProjection == null || running) {
return false;
}
initRecorder();
createVirtualDisplay();
mediaRecorder.start();
running = true;
return true;
}
private void initRecorder() throws JSONException {
CamcorderProfile mProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_1080P);
//判断是不是需要声音
if (this.dataOption.get("voiceStatus") == "1"){
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//设置声音
}
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
//判断是不是需要声音
if (this.dataOption.get("voiceStatus") == "1"){
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
}
mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mediaRecorder.setVideoSize(this.width, this.height);
mediaRecorder.setVideoFrameRate(30);
mediaRecorder.setVideoEncodingBitRate(mProfile.videoBitRate);
//设置文件输出路径
mediaRecorder.setOutputFile('');
try {
mediaRecorder.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 创建一个录屏 Virtual
*/
private void createVirtualDisplay() {
//VirtualDisplay录屏功能,主要通过这个类来实现,VirtualDisplay对应虚拟Display,主要用来进行屏幕录制等相关功能
virtualDisplay = mediaProjection
.createVirtualDisplay("mediaprojection", width, height, dpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mediaRecorder
.getSurface(), null, null);
}
6. 结束录屏
/**
* 结束录屏
*/
public boolean stopRecord() {
if (!running) {
return false;
}
running = false;
mediaRecorder.stop();
mediaRecorder.reset();
virtualDisplay.release();
mediaProjection.stop();
return true;
}
标签:总结,mediaRecorder,IBinder,service,录屏,Intent,android,public From: https://blog.csdn.net/j15087159186/article/details/139626883