Android 13 起强制启用分区存储(Scoped Storage),WRITE_EXTERNAL_STORAGE 和 MANAGE_EXTERNAL_STORAGE 权限已废弃或受限,需改用媒体权限 + 共享目录(如 Downloads)+ 应用专属路径方式安全保存 MP4 录制文件。
android 13 起强制启用分区存储(scoped storage),`write_external_storage` 和 `manage_external_storage` 权限已废弃或受限,需改用媒体权限 + 共享目录(如 downloads)+ 应用专属路径方式安全保存 mp4 录制文件。
自 Android 11(R,API 30)起,Google 逐步收紧外部存储访问策略;至 Android 13(Tiramisu,API 33),MANAGE_EXTERNAL_STORAGE 权限已被严格限制(仅限特定场景且需 Google 审核),传统 Environment.getExternalStoragePublicDirectory() 调用虽未完全失效,但必须配合正确的运行时权限与路径规范,否则 MediaRecorder.setOutputFile() 将抛出 SecurityException 或静默失败。
✅ 正确适配方案(API 33 兼容)
1. 声明必要权限(AndroidManifest.xml)
<!-- 必选:录制音频 --> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <!-- 必选:访问媒体文件(含写入 Downloads 目录) --> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <!-- 可选:仅当需访问其他应用媒体文件时才声明(非本例必需) --> <!-- <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> -->
⚠️ 注意:WRITE_EXTERNAL_STORAGE、READ_EXTERNAL_STORAGE、MANAGE_EXTERNAL_STORAGE 在 targetSdkVersion ≥ 33 时不应再声明(Android Studio 会警告),且 MANAGE_EXTERNAL_STORAGE 已被 Google Play 政策禁止普通应用使用。
2. 运行时动态申请媒体权限(Kotlin)
private fun requestMediaPermissions() {
val permissions = arrayOf(
Manifest.permission.RECORD_AUDIO,
Manifest.permission.READ_MEDIA_VIDEO,
Manifest.permission.READ_MEDIA_IMAGES
)
if (!allPermissionsGranted(permissions)) {
ActivityCompat.requestPermissions(this, permissions, REQUEST_PERMISSIONS)
} else {
startScreenRecording()
}
}
private fun allPermissionsGranted(permissions: Array<String>): Boolean {
return permissions.all {
ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED
}
}3. 使用 Downloads 目录 + 应用子目录保存文件(推荐)
Android 13 允许应用无需特殊权限即可向 Downloads 目录写入文件(系统自动授予 WRITE_MEDIA_STORAGE 隐式权限),但为保持组织性与兼容性,建议创建应用专属子目录:
YZTurboWebAndroid 高性能 Android WebView 容器 SDK 接入。用于在 Android 项目中集成 WebView 容器,实现: (1) WebView 预加载与复用,提升 H5 页面加载速度 (2) 离线包管理,拦截请求优先命中本地资源 (3) JS Bridge 双向通信,Na...
@SuppressLint("UsableSpace")
private fun getRecordingFile(): File? {
val downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
val appDir = File(downloadsDir, "MyScreenRecorder")
// 确保目录存在
if (!appDir.exists()) {
appDir.mkdirs() // 使用 mkdirs() 支持多级创建
}
val timestamp = SimpleDateFormat("dd-MM-yyyy-HH_mm_ss", Locale.getDefault()).format(Date())
return File(appDir, "ScreenRecord_$timestamp.mp4")
}4. 更新 initRecorder() 中的输出路径
替换原 setOutputFile() 调用:
private fun initRecorder() {
try {
mMediaRecorder?.apply {
setAudioSource(MediaRecorder.AudioSource.MIC)
setVideoSource(MediaRecorder.VideoSource.SURFACE)
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) // 推荐 MP4 格式而非 3GP
setOutputFile(getRecordingFile()?.absolutePath ?: return) // ✅ 关键:使用安全路径
setVideoSize(DISPLAY_WIDTH, DISPLAY_HEIGHT)
setVideoEncoder(MediaRecorder.VideoEncoder.H264)
setAudioEncoder(MediaRecorder.AudioEncoder.AAC) // AMR_NB 已过时,推荐 AAC
setVideoEncodingBitRate(2_000_000) // 提升码率以改善画质
setVideoFrameRate(30)
setOrientationHint(ORIENTATIONS[windowManager.defaultDisplay.rotation + 90])
prepare()
}
} catch (e: Exception) {
Log.e(TAG, "MediaRecorder prepare failed", e)
Toast.makeText(this, "录制初始化失败: ${e.message}", Toast.LENGTH_LONG).show()
}
}5. (可选)适配 Android 10+ 的 MediaStore API(更现代方案)
若需更高可靠性或支持云同步,可改用 MediaStore 插入视频:
private fun createMediaStoreUri(context: Context): Uri? {
val values = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "ScreenRecord_${System.currentTimeMillis()}.mp4")
put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_MOVIES + "/MyScreenRecorder")
}
return context.contentResolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values)
}
// 然后 setOutputFile() 传入该 Uri? 关键注意事项
- 不要依赖 MANAGE_EXTERNAL_STORAGE:它在 Android 13 上对大多数应用不可用,且 Google Play 拒绝上架。
- 避免硬编码 /sdcard/ 或 getExternalStorageDirectory():已被弃用,触发 ScopedStorage 限制。
- Environment.getExternalStoragePublicDirectory() 仍可用,但仅限 DIRECTORY_DOWNLOADS、DIRECTORY_MOVIES 等公共目录,且需确保目标目录可写(通常默认允许)。
- 测试真机环境:模拟器可能未完全模拟分区存储行为,务必在 Android 13 实机验证。
- 错误处理必做:MediaRecorder.prepare() 可能因路径无效、磁盘满、权限缺失等失败,需捕获并提示用户。
通过以上调整,您的屏幕录制功能即可在 Android 13 及更高版本稳定运行,符合最新存储最佳实践,兼顾安全性与兼容性。

















