Android实现录屏MediaProjection以及相关异常解决
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android实现录屏MediaProjection以及相关异常解决相关的知识,希望对你有一定的参考价值。
参考技术A 需要实现一个手机的录屏功能,于是从网上找了些相关资料和源码,发现跑不起来,于是开始dubug,发现坑还是很多的,这里记录一下实现过程和一些些遇到的异常以及一个我调整完可以跑的Demo。首先在androidManifest中静态配置权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
然后在Activity中动态申请
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED)
ActivityCompat.requestPermissions(this,
new String[] Manifest.permission.WRITE_EXTERNAL_STORAGE, STORAGE_REQUEST_CODE);
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED)
ActivityCompat.requestPermissions(this,
new String[] Manifest.permission.RECORD_AUDIO, AUDIO_REQUEST_CODE);
因为项目中需要用到一个自定义的Application,所以要需要配置一个全局的Application,同样在AndroidManiest中在application添加自定义的类名,如果在里面启动服务了也要一并配置。
<application
android:name=".RecordApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</application>
然后可以使用封装好的实现其录屏功能的service,这个封装类是网上找的,看很多人在用,我解决了一些异常,并根据自己需求修改了一下。
其中主要异常有:
1.mediaRecorder报空指针,解决方案,在声明的时候声明为静态
private static MediaRecorder mediaRecorder;
2.mediaRecorder.start()方法异常,在每次调用stop时要先调用
mediaRecorder.stop();
mediaRecorder.release();
两个方法,并将
mediaRecorder = null。
mediaRecorder.setAudiosource(MediaRecorder.AudioSource.MIC)异常,这里是设置音频源,可尝试将参数改为
MediaRecorder.AudioSource.DEFAULT
4.stop方法异常,如果是running状态不正常,可能是其状态丢失,需要将声明的running也改为静态的
0.增加需求,在生成视频时大部分人都会根据mediaRecorder.setVideoSize(width, height);方法来定死视频大小,导致一些手机会解析不了,或者是视频比屏幕小,这里提供一种根据屏幕大小动态设置视频大小的方法。
这里就要用到我们之前定义的全局的Application,然后调用getInstance()获取其实例,
然后通过
DisplayMetrics dm = RecordApplication.getInstance().getResources().getDisplayMetrics();
private int width = dm.widthPixels;
private int height = dm.heightPixels;
private int dpi = dm.densityDpi;
来获取屏幕的长、宽和dpi的值,这里不用WindowsManager方法是因为我是在非Activity去获取屏幕长宽的,所以用了getDisplayMetrics();
这样这个功能基本就是实现了。
Demo地址: https://github.com/han103070/Screencap
Android8.0及以上基于系统MediaRecorder实现录屏最基础步骤
1.申请到必须的相关权限
- xml配置,另外需要动态获取权限才行
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<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" />
- notification权限
private void notificationPermission(Context context) //判断应用的通知权限是否打开,返回Boolean值
if (!NotificationManagerCompat.from(context).areNotificationsEnabled())
Intent localIntent = new Intent();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) //8.0及以上
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
localIntent.setData(Uri.fromParts("package", context.getPackageName(), null));
else
localIntent.setAction("android.settings.APP_NOTIFICATION_SETTINGS");
localIntent.putExtra("app_package", context.getPackageName());
localIntent.putExtra("app_uid", context.getApplicationInfo().uid);
context.startActivity(localIntent);
2.编写录屏前台服务
- 在绑定时初始化的数据,包括通过传递过来的数据获取到mediaProjection
@Override
public IBinder onBind(Intent intent)
//创建前台Notification保护
createNotificationChannel();
//初始化MediaProjection
initMediaProjection(intent);
return new RecordBinder();
private void initMediaProjection(Intent intent)
int mResultCode = intent.getIntExtra("code", -1);
Intent mResultData = intent.getParcelableExtra("data");
MediaProjectionManager mMediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
mediaProjection = mMediaProjectionManager.getMediaProjection(mResultCode, Objects.requireNonNull(mResultData));
private void createNotificationChannel()
Notification.Builder builder = new Notification.Builder(this.getApplicationContext());
Intent nfIntent = new Intent(this, MainActivity.class); //点击后跳转的界面,可以设置跳转数据
builder.setContentIntent(PendingIntent.getActivity(this, 0, nfIntent, 0)) // 设置PendingIntent
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.mipmap.ic_launcher)) // 设置下拉列表中的图标(大图标)
.setContentTitle("屏幕录制") // 设置下拉列表里的标题
.setSmallIcon(R.mipmap.ic_launcher) // 设置状态栏内的小图标
.setContentText("当前正在录屏中...") // 设置上下文内容
.setWhen(System.currentTimeMillis()); // 设置该通知发生的时间
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
String notification_id = "notification_screenCap_id";
String notification_name = "notification_screenCap_name";
builder.setChannelId(notification_id);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel(notification_id, notification_name, NotificationManager.IMPORTANCE_LOW);
notificationManager.createNotificationChannel(channel);
Notification notification = builder.build();
notification.defaults = Notification.DEFAULT_SOUND; //设置为默认的声音
int notification_id1 = 101;
startForeground(notification_id1, notification);
- 开启录屏与停止录屏基础方法
public boolean startRecord()
if (mediaProjection == null || running)
return false;
else
try
initRecorder();
catch (IOException e)
e.printStackTrace();
running = false;
return false;
createVirtualDisplay();
mediaRecorder.start();
running = true;
return true;
public boolean stopRecord()
if (running)
mediaRecorder.stop();
mediaRecorder.release();
virtualDisplay.release();
mediaProjection.stop();
mediaRecorder = null;
return true;
return false;
private void createVirtualDisplay()
virtualDisplay = mediaProjection.createVirtualDisplay("MainScreen", width, height, dpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mediaRecorder.getSurface(), null, null);
private void initRecorder() throws IOException
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
videoName = System.currentTimeMillis() + ".mp4";
mediaRecorder.setOutputFile(createSaveVideoFile());
mediaRecorder.setVideoSize(width, height);
mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mediaRecorder.setVideoEncodingBitRate(5 * 1024 * 1024);
mediaRecorder.setVideoFrameRate(30);
mediaRecorder.prepare();
3.申请录屏服务权限
projectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
Intent captureIntent = projectionManager.createScreenCaptureIntent();
startActivityForResult(captureIntent, RECORD_REQUEST_CODE);
4.成功后绑定录屏服务
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RECORD_REQUEST_CODE && resultCode == RESULT_OK)
Intent intent = new Intent(this, RecordService.class);
intent.putExtra("code", resultCode);
intent.putExtra("data", data);
bindService(intent, connection, BIND_AUTO_CREATE);
private final ServiceConnection connection = new ServiceConnection()
@Override
public void onServiceConnected(ComponentName className, IBinder service)
RecordService.RecordBinder binder = (RecordService.RecordBinder) service;
recordService = binder.getRecordService();
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
//设置录屏分辨率
recordService.setConfig(metrics.widthPixels, metrics.heightPixels, metrics.densityDpi);
recordService.startRecord();
startBtn.setEnabled(true);
startBtn.setText(recordService.isRunning() ? "停止录屏" : "开始录屏");
@Override
public void onServiceDisconnected(ComponentName arg0)
;
这样后就可以通过ServiceConnection获取到服务的引用了,下面就可以愉快的进行业务逻辑的交互开发吧。
5.初步演示效果
转载注明:https://blog.csdn.net/u014614038/article/details/117699817
以上是关于Android实现录屏MediaProjection以及相关异常解决的主要内容,如果未能解决你的问题,请参考以下文章
Android实现录屏直播ScreenRecorder的简单分析
Android实现录屏MediaProjection以及相关异常解决