Android屏幕录制技术:MediaProjection与MediaRecorder实战
1. Android录屏技术背景与核心组件解析在移动应用开发中屏幕录制功能的需求日益增长无论是游戏直播、操作演示还是远程协助场景都离不开这项基础能力。Android平台从5.0Lollipop开始通过MediaProjection API提供了系统级的录屏支持配合MediaRecorder实现了高效的屏幕捕获与编码流水线。MediaProjection作为录屏功能的核心本质上是一个令牌(token)系统。当用户授权应用进行屏幕捕获后系统会返回一个MediaProjection实例这个实例包含了进行屏幕捕获所需的所有权限和上下文信息。其工作流程可以类比为电影院放映机——MediaProjection是放映机本身而VirtualDisplay则是投射到墙上的光影。MediaRecorder则是Android多媒体框架中的瑞士军刀负责将原始视频数据编码压缩为可存储的格式。它支持H.264、H.265等主流编码格式能够直接接收来自VirtualDisplay的表面(Surface)数据流。这种设计使得屏幕内容可以不经过应用层直接进入编码管道大幅提升了录制效率。2. 实现方案设计与权限配置2.1 基础工程配置在AndroidManifest.xml中需要声明以下关键权限uses-permission android:nameandroid.permission.FOREGROUND_SERVICE/ uses-permission android:nameandroid.permission.WRITE_EXTERNAL_STORAGE/ uses-permission android:nameandroid.permission.RECORD_AUDIO/ !-- 如需录制音频 --对于Android 10设备必须在manifest中添加屏幕捕获服务声明service android:name.ScreenRecordService android:foregroundServiceTypemediaProjection/2.2 权限获取流程屏幕录制需要用户显式授权这个流程必须通过Activity触发private fun requestScreenCapture() { val mediaProjectionManager getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager startActivityForResult( mediaProjectionManager.createScreenCaptureIntent(), REQUEST_CODE_SCREEN_CAPTURE ) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode REQUEST_CODE_SCREEN_CAPTURE resultCode RESULT_OK) { data?.let { startRecordingService(it) } } }重要提示用户授权是一次性的每次应用重启后都需要重新获取。实际开发中应该缓存授权结果避免频繁弹窗影响用户体验。3. 核心实现代码解析3.1 MediaRecorder配置创建适配当前设备的最佳录制参数private fun setupMediaRecorder(): MediaRecorder { return MediaRecorder().apply { setVideoSource(MediaRecorder.VideoSource.SURFACE) setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) // 自适应视频参数配置 val displayMetrics resources.displayMetrics val bitrate displayMetrics.widthPixels * displayMetrics.heightPixels * 5 // 基础比特率计算 setVideoEncoder(MediaRecorder.VideoEncoder.H264) setVideoEncodingProfileLevel( MediaCodecInfo.CodecProfileLevel.AVCProfileHigh, MediaCodecInfo.CodecProfileLevel.AVCLevel42 ) setVideoSize(displayMetrics.widthPixels, displayMetrics.heightPixels) setVideoFrameRate(30) setVideoEncodingBitRate(bitrate) // 输出文件配置 val outputDir getExternalFilesDir(Environment.DIRECTORY_MOVIES) ?: filesDir setOutputFile(File(outputDir, record_${System.currentTimeMillis()}.mp4).absolutePath) } }3.2 VirtualDisplay创建将屏幕内容投射到MediaRecorder的Surfaceprivate fun createVirtualDisplay( mediaProjection: MediaProjection, recorder: MediaRecorder ): VirtualDisplay { val metrics resources.displayMetrics return mediaProjection.createVirtualDisplay( ScreenCapture, metrics.widthPixels, metrics.heightPixels, metrics.densityDpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, recorder.surface, null, null ) }3.3 服务端完整实现Foreground Service的实现要点class ScreenRecordService : Service() { private lateinit var mediaProjection: MediaProjection private lateinit var mediaRecorder: MediaRecorder private lateinit var virtualDisplay: VirtualDisplay override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { startForeground( NOTIFICATION_ID, createNotification().build() ) mediaProjection (getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager) .getMediaProjection(Activity.RESULT_OK, intent!!) mediaRecorder setupMediaRecorder().apply { prepare() } virtualDisplay createVirtualDisplay(mediaProjection, mediaRecorder) mediaRecorder.start() return START_NOT_STICKY } private fun stopRecording() { try { mediaRecorder.stop() } catch (e: IllegalStateException) { Log.e(TAG, Stop failed: ${e.message}) } finally { cleanupResources() } } private fun cleanupResources() { virtualDisplay.release() mediaProjection.stop() mediaRecorder.release() stopForeground(true) stopSelf() } }4. 高级优化技巧4.1 性能调优参数根据不同设备配置动态调整参数fun getOptimalFrameRate(caps: MediaCodecInfo.CodecCapabilities): Int { return caps.videoCapabilities.supportedFrameRates .filter { it 60 } // 限制最大帧率 .maxOrNull() ?: 30 } fun getOptimalBitrate(width: Int, height: Int): Int { return when { width * height 1920 * 1080 - 8000000 // 1080p width * height 1280 * 720 - 4000000 // 720p else - 2000000 // 低分辨率 } }4.2 音频同步录制实现音视频同步录制private fun setupMediaRecorderWithAudio(): MediaRecorder { return setupMediaRecorder().apply { setAudioSource(MediaRecorder.AudioSource.MIC) setAudioEncoder(MediaRecorder.AudioEncoder.AAC) setAudioChannels(2) setAudioSamplingRate(44100) setAudioEncodingBitRate(128000) } }4.3 动态分辨率适配根据设备性能自动降级fun getAdjustedResolution(originalWidth: Int, originalHeight: Int): PairInt, Int { return when { Runtime.getRuntime().availableProcessors() 4 - originalWidth / 2 to originalHeight / 2 getTotalMemory() 2 * 1024 - (originalWidth * 0.75).toInt() to (originalHeight * 0.75).toInt() else - originalWidth to originalHeight } }5. 常见问题排查指南5.1 权限问题排查表现象可能原因解决方案立即崩溃未声明FOREGROUND_SERVICE权限添加权限声明黑屏录制未正确获取MediaProjection检查onActivityResult处理文件为空存储权限未授予动态请求WRITE_EXTERNAL_STORAGE5.2 编码问题处理视频花屏问题检查H264 profile/level配置是否超出设备支持范围尝试降低帧率或分辨率添加关键帧间隔配置mediaRecorder.setVideoEncodingIFrameInterval(2) // 每2秒一个关键帧音频不同步问题确保音频采样率设置为44100Hz检查时间戳处理mediaRecorder.setCaptureRate(30.0) // 明确设置捕获速率5.3 设备兼容性问题特殊机型适配fun checkDeviceSpecificIssues() { when (Build.MANUFACTURER.lowercase()) { xiaomi - { // 小米设备需要额外设置 mediaRecorder.setVideoEncodingProfileLevel( MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline, MediaCodecInfo.CodecProfileLevel.AVCLevel3 ) } huawei - { // 华为设备需要限制比特率 mediaRecorder.setVideoEncodingBitRate(bitrate / 2) } } }6. 前沿技术扩展6.1 低延迟直播推流通过MediaCodec直接获取原始数据mediaProjection.createVirtualDisplay( ScreenCapture, width, height, dpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, surface, // 自定义Surface连接到MediaCodec null, null )6.2 动态内容分析实时处理屏幕内容val imageReader ImageReader.newInstance( width, height, PixelFormat.RGBA_8888, 2 ).apply { setOnImageAvailableListener({ reader - val image reader.acquireLatestImage() // 处理图像数据 image.close() }, handler) } mediaProjection.createVirtualDisplay( Analysis, width, height, dpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, imageReader.surface, null, null )6.3 多源合成录制结合摄像头画面val textureView TextureView(context).apply { setSurfaceTextureListener(object : TextureView.SurfaceTextureListener { override fun onSurfaceTextureAvailable(st: SurfaceTexture, w: Int, h: Int) { val surface Surface(st) // 将摄像头画面绘制到此surface } }) } // 在MediaRecorder配置中添加摄像头源 mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE) mediaRecorder.setInputSurface(textureView.surfaceTexture)在实际项目中我发现正确处理MediaProjection的生命周期至关重要。特别是在Android 12及以上版本中当用户切换应用或锁屏时系统会主动停止MediaProjection此时需要妥善处理中断事件并给用户适当的反馈。一个实用的技巧是在注册MediaProjection.Callback时保存关键状态以便在意外中断后能够恢复录制。

相关新闻

人生容错率到底有多大:一个文科生硬着头皮变专家的真实故事

人生容错率到底有多大:一个文科生硬着头皮变专家的真实故事

文章目录前言一、你被"独木桥认知"骗了二、那个文科生的故事:硬着头皮就够了三、什么情况下容错率真的低四、旧轨道在消失,这不是你的错五、给技术人的建议:怎么扩大自己的容错率总结前言 “人生的容错率大到无法想象”——这句话…

2026/10/18 15:54:47 阅读更多 →
3分钟掌握PowerToys Awake:Windows防休眠智能解决方案

3分钟掌握PowerToys Awake:Windows防休眠智能解决方案

3分钟掌握PowerToys Awake:Windows防休眠智能解决方案 【免费下载链接】PowerToys Microsoft PowerToys is a collection of utilities that supercharge productivity and customization on Windows 项目地址: https://gitcode.com/GitHub_Trending/po/PowerToys…

2026/10/17 9:00:38 阅读更多 →
Poppler-Windows:Windows平台PDF自动化处理的终极解决方案

Poppler-Windows:Windows平台PDF自动化处理的终极解决方案

Poppler-Windows:Windows平台PDF自动化处理的终极解决方案 【免费下载链接】poppler-windows Download Poppler binaries packaged for Windows with dependencies 项目地址: https://gitcode.com/gh_mirrors/po/poppler-windows 在数字化转型浪潮中&#xf…

2026/10/18 19:01:36 阅读更多 →

最新新闻

增量索引实战:新文档怎么做到分钟级可检索

增量索引实战:新文档怎么做到分钟级可检索

个人主页&#xff1a;> for_ever_love__ <&#xff08;欢迎各位大佬莅临&#x1f60a;&#xff09; 其他栏目: > 大模型开发从0到1 < 其他栏目: > iOS项目总结大全 < 其他栏目: > 我想学python了 < 其他栏目: > iOS UI < 文章目录增量索引实战&a…

2026/10/18 21:45:10 阅读更多 →
Python多模态情感分析实战:CCA对齐+门控融合工程实现

Python多模态情感分析实战:CCA对齐+门控融合工程实现

简介&#xff1a;本资源是一套完整的多模态融合情感分析实战项目&#xff0c;面向计算机专业本科生及人工智能初学者&#xff0c;聚焦文本、语音、图像、视频四模态数据的情感联合建模与分析&#xff0c;适用于毕业设计、期末大作业及课程设计等高要求实践场景。压缩包共20个文…

2026/10/18 21:45:10 阅读更多 →
Onyx开源企业搜索深度解析:RAG与知识管理实践

Onyx开源企业搜索深度解析:RAG与知识管理实践

1. 为什么需要 Onyx&#xff1a;企业知识检索的三个死结1.1 知识分散&#xff1a;搜索引擎覆盖不到的角落先说我见过最多的场景。一个几十人的团队&#xff0c;文档其实不少&#xff1a;Wiki 里躺着架构设计、聊天工具里埋着半年前的决策、在线文档里散落着各种会议纪要和排期表…

2026/10/18 21:45:10 阅读更多 →
国产大模型对比:文心、豆包、通义...哪家最好?基于最公允大模型排行榜细致整理

国产大模型对比:文心、豆包、通义...哪家最好?基于最公允大模型排行榜细致整理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 21:45:10 阅读更多 →
协同过滤电影推荐系统毕设:Python实现核心算法与工程避坑全解析

协同过滤电影推荐系统毕设:Python实现核心算法与工程避坑全解析

简介&#xff1a;基于协同过滤推荐算法的电影推荐系统Python毕业设计项目&#xff0c;面向计算机相关专业毕业生、课程设计及期末大作业学生&#xff0c;也适合有Python基础、希望实战推荐系统开发的进阶学习者。整包共2000个文件&#xff0c;以py源码、html页面、css样式、js脚…

2026/10/18 21:45:10 阅读更多 →
大模型工具使用与函数调用:从原理到落地的完整指南

大模型工具使用与函数调用:从原理到落地的完整指南

1. 从"能聊天"到"能干活"&#xff1a;工具使用与函数调用到底在解决什么问题大模型刚火起来那阵子&#xff0c;大家最直观的体验就是"能聊"。你问它答&#xff0c;写诗写文案写代码&#xff0c;样样都行。但真把它扔进一个实际业务里&#xff0c…

2026/10/18 21:44:10 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →