Android MediaProjection实战从权限申请到VirtualDisplay的完整避坑指南在移动应用开发中屏幕录制和截图功能已经成为许多应用的核心需求。无论是教育类应用的课程录制、游戏直播还是企业协作工具的屏幕共享都离不开这一关键技术。然而Android平台上的MediaProjection API虽然强大却隐藏着不少坑让不少开发者头疼不已。1. 权限申请的正确姿势权限申请是MediaProjection使用的第一步也是最容易出错的地方。很多开发者在这里就遇到了各种奇怪的问题比如弹窗不显示、回调不触发等。1.1 理解MediaProjection的权限机制与普通权限不同MediaProjection需要用户显式授权。这意味着必须通过系统弹窗获取用户同意无法通过普通权限请求方式获取授权是一次性的每次应用启动都需要重新获取正确的权限请求代码如下private lateinit var mediaProjectionManager: MediaProjectionManager fun requestScreenCapturePermission() { mediaProjectionManager getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager startActivityForResult( mediaProjectionManager.createScreenCaptureIntent(), SCREEN_CAPTURE_REQUEST_CODE ) }1.2 常见问题排查清单问题现象可能原因解决方案弹窗不显示未在主线程调用确保在UI线程执行请求回调不触发requestCode不匹配检查onActivityResult中的requestCode立即返回取消应用处于后台确保应用在前台时请求权限提示Android 10对后台启动Activity有严格限制务必确保应用可见时请求权限2. VirtualDisplay配置的深度解析创建VirtualDisplay是MediaProjection的核心环节也是黑屏问题的高发区。2.1 关键参数详解VirtualDisplay的创建涉及多个重要参数val virtualDisplay mediaProjection.createVirtualDisplay( ScreenCapture, // 显示名称 width, // 宽度像素 height, // 高度像素 densityDpi, // 显示密度 DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, // 标志位 surface, // 输出Surface null, // 回调 null // Handler )其中最容易出问题的是标志位和Surface配置VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR自动镜像内容适合大多数场景VIRTUAL_DISPLAY_FLAG_PUBLIC允许内容被其他应用看到VIRTUAL_DISPLAY_FLAG_SECURE保护敏感内容2.2 解决黑屏问题的关键黑屏通常由以下原因导致Surface未正确初始化确保Surface有效且尺寸匹配标志位冲突避免同时使用互斥的标志位生命周期管理不当MediaProjection和VirtualDisplay需要在适当时候释放推荐的生命周期管理方式override fun onDestroy() { super.onDestroy() virtualDisplay?.release() mediaProjection?.stop() }3. 跨版本兼容性处理Android不同版本对MediaProjection的实现有细微差别需要特别注意。3.1 Android 10的变化从Android 10开始系统对后台启动Activity和权限获取做了更严格的限制必须在前台服务中运行录屏需要添加FOREGROUND_SERVICE权限必须显示通知告知用户正在录屏示例前台服务配置service android:name.ScreenCaptureService android:foregroundServiceTypemediaProjection /3.2 版本适配检查清单[ ] 检查Build.VERSION.SDK_INT判断版本[ ] Android 10添加前台服务类型[ ] 处理不同版本下的Surface配置差异[ ] 适配不同DPI设备的显示问题4. 性能优化与高级技巧4.1 内存与CPU优化录屏是资源密集型操作优化至关重要降低分辨率不必总是使用屏幕原生分辨率调整帧率30fps通常足够流畅使用硬件编码优先选择H.264/H.265硬件编码// 配置MediaRecorder使用硬件编码 mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264) mediaRecorder.setVideoEncodingProfile( CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH))4.2 高级应用场景掌握了基础功能后还可以实现更复杂的功能选择性录屏只录制特定区域或窗口音频同步将系统声音或麦克风输入与视频同步实时处理对视频流进行实时滤镜或分析处理实现音频同步的示例// 配置音频源 mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC) mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC) // 确保音频和视频参数匹配 mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)5. 实战问题解决方案在实际项目中我们经常会遇到一些棘手的问题。以下是几个典型场景的解决方案。5.1 处理屏幕旋转屏幕旋转会导致VirtualDisplay配置失效需要动态调整监听配置变化重新创建VirtualDisplay保持Surface的有效性override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) // 重新初始化VirtualDisplay setupVirtualDisplay() }5.2 多显示器支持对于支持多显示器的设备需要特别处理val displayManager getSystemService(Context.DISPLAY_SERVICE) as DisplayManager val displays displayManager.displays // 选择主显示器或其他显示器 val primaryDisplay displays.find { it.displayId Display.DEFAULT_DISPLAY }5.3 低延迟优化对于需要实时性的应用如游戏直播可以尝试使用MediaCodec直接编码降低GOP长度调整关键帧间隔mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1) mediaFormat.setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh)在实际项目中我发现最容易被忽视的是正确释放资源。不恰当的释放顺序会导致内存泄漏甚至系统稳定性问题。建议按照以下顺序释放先停止VirtualDisplay然后释放MediaProjection最后关闭Surface另外对于需要长时间运行的录屏功能建议添加心跳检测机制定期检查各组件状态确保录屏过程稳定可靠。