Android最全深入浅出,Andorid 端屏幕采集技术实践,2024年Android开发学习路线

最后

考虑到文章的篇幅问题,我把这些问题和答案以及我多年面试所遇到的问题和一些面试资料做成了PDF文档

喜欢的朋友可以关注、转发、点赞 感谢!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

前言

随着全球产业链线上化和数字化的加速,移动端实时屏幕共享在各行各业场景下都有了广泛的应用,比如在线教育、视频会议、远程业务咨询、手游直播。而屏幕采集则是实现实时屏幕共享流程中的第一步,本篇技术分享就来跟大家讲讲拍乐云在 Andorid 端屏幕采集的经验实践。

背景

Android 从 4.0 开始就提供了手机录屏方法,但是需要 root 权限。从 5.0 开始,Google 开放了系统录屏API:MediaProjection 和 MediaProjectionManager,不需要 root 权限,但是会弹出录屏权限申请框,用户同意后才能开始录屏,类似 Android6.0 之后权限申请流程。 鉴于目前市面上5.0以下的 Android 手机占比很低且屏幕采集需要 root 权限实现复杂,接下来我们主要介绍 Android5.0 及以上版本的屏幕采集原理。 试想一下,一套完整的屏幕采集流程应该是怎样的?屏幕数据源(生产者)在缓冲区产生数据,屏幕数据消费者从缓冲区提取数据使用。不同的消费者可以实现不同的功能,比如录屏保存和录屏直播(屏幕共享)。这些关键的角色在Android 端又是由谁来扮演呢? VirtualDisplayVirtualDisplay 是 Android 上的虚拟显示器。本文里VirtualDisplay 的作用就是抓取屏幕上显示的内容,是屏幕数据的生产者。 Surface 在 Android 的窗口实现里,Surface 对应了一块屏幕数据缓冲区,屏幕数据生产者可以在 Surface 上生产数据,消费者则从 Surface 中提取数据使用。 屏幕采集流程

介绍完以上关键角色,我们大致可以画出一套屏幕采集流程图:

QQ图片20210514140350.png

下面逐步介绍代码实现。

一、获取MediaProjection

首先需要获取 MediaProjectionManager 服务,然后通过 MediaProjectionManager 服务,获取一个申请屏幕采集权限的 Intent 并启动屏幕采集申请权限界面: mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE); Intent intent = mediaProjectionManager.createScreenCaptureIntent(); startActivityForResult(intent, SCREEN_CAPTURE_REQUEST_CODE); 启动的屏幕采集权限申请界面如下:

QQ图片20210514140548.png

用户允许(点击立即开始)后,在 onActivityResult 回调里根据返回的resultCode和 data 获取 MediaProjection:

protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data);

if (requestCode == SCREEN_CAPTURE_REQUEST_CODE && resultCode == Activity.RESULT_OK) { mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data); } } 需要特别注意的是,在 targetSdkVersion 大于等于29时,系统加强了对屏幕采集的限制,必须先启动相应的前台 Service,才能正常调用 getMediaProjection 方法,否则会抛异常: java.lang.SecurityException: Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION 查看系统源码发现以下条件语句如果都为 true 则抛出以上异常: if (REQUIRE_FG_SERVICE_FOR_PROJECTION //1.默认为true && requiresForegroundService() //2.当前APP需要启动前台Service && !mActivityManagerInternal.hasRunningForegroundService( //3.当前应用没有启动前台service uid, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION)) { throw new SecurityException(“Media projections require a foreground service” + " of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION"); }

//APP TargetSdkVersion大于等于29并且不是特权应用(特权应用一般是系统应用),则返回true(需要启动前台service) boolean requiresForegroundService () { return mTargetSdkVersion >= Build.VERSION_CODES.Q && !mIsPrivileged; } 前台 Service 配置参考如下:

二、构造Surface

1.如果屏幕采集数据用来录制视频,那么消费者可以是 MediaRecoder,相应地 Surface 由 MediaRecoder 提供: Surface surface = mediaRecorder.getSurface(); 2.如果屏幕采集数据用来屏幕共享(录屏直播),那么消费者可以是类似 MediaCodec 这样的编码器,相应地 Surface 由 MediaCodec 提供: Surface surface = mediaCodec.createInputSurface(); 3.如果需要将屏幕采集数据显示在UI界面 SurfaceView 上的话,Surface可以通过以下方式生成: SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surface); Surface surface = surfaceView.getHolder().getSurface(); 4.如果想要更加灵活的掌控整个屏幕采集流程,Surface 还可以通过 SurfaceTexture 生成: SurfaceTexture surfaceTexture = new SurfaceTexture(textureId); surfaceTexture.setOnFrameAvailableListener(new OnFrameAvailableListener() {

@Override public void onFrameAvailable(SurfaceTexture surfaceTexture) {

} }, handler); Surface surface = new Surface(surfaceTexture); 这里简单介绍下SurfaceTexture 。SurfaceTexture 可以用来捕获视频流中的图像帧,当 SurfaceTexture 中有数据更新时,会触发onFrameAvailable 回调,此时可以调用 updateTexImage 方法从视频流数据中更新当前数据帧。

三、创建VirtualDisplay

MediaProjection 有现成的API可以调用: public VirtualDisplay createVirtualDisplay(String name, int width, int height, int dpi, int flags, Surface surface, VirtualDisplay.Callback callback, Handler handler) {

DisplayManager dm = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); return dm.createVirtualDisplay(this, name, width, height, dpi, surface, flags, callback, handler, null /* uniqueId */); } 参数说明文档如下:

QQ图片20210514141549.png

各参数 Android 官方文档都有较详细的说明,其中 flag 和 surface 这里再额外说明下:

flag是VirtualDisplay的标记位,一般取VIRTUAL_DISPLAY_FLAG_PUBLIC即可; surface 也就是上文提到的屏幕数据缓冲区,一般由消费者提供。

最后

有任何问题,欢迎广大网友一起来交流,分享高阶Android学习视频资料和面试资料包~

偷偷说一句:群里高手如云,欢迎大家加群和大佬们一起交流讨论啊!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值