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时保存关键状态以便在意外中断后能够恢复录制。