API 29及以上版本下,如何使用Kotlin为MediaStore.ACTION_VIDEO_CAPTURE录制的视频设置预定义存储名称
如何在API 29+下为录制的视频指定预定义名称
在API 29及以上版本,Android引入了分区存储机制,WRITE_EXTERNAL_STORAGE权限不再生效,直接传入文件路径给MediaStore.EXTRA_OUTPUT肯定行不通——因为系统相机应用没有权限访问你指定的普通文件路径。正确的做法是通过MediaStore先在公共媒体库中创建一个带有自定义名称的条目,拿到对应的Content Uri,再把这个Uri传给相机Intent。
下面是完整的实现步骤和代码示例:
步骤1:准备视频存储元数据并插入MediaStore
首先,你需要定义视频的显示名称、存储路径(比如公共DCIM目录下的自定义子文件夹)、MIME类型等信息,然后通过ContentResolver插入到MediaStore.Video.Media.EXTERNAL_CONTENT_URI中,获取对应的Uri。
private fun createCustomVideoUri(displayName: String): Uri? { val videoDetails = ContentValues().apply { // 设置视频显示名称(就是你想要的预定义名称,记得加.mp4后缀) put(MediaStore.Video.Media.DISPLAY_NAME, displayName) // 设置MIME类型 put(MediaStore.Video.Media.MIME_TYPE, "video/mp4") // 设置存储路径:比如放在DCIM下的MyVideos文件夹,也可以用其他公共目录 put( MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM + "/MyVideos" ) // 可选:添加时间戳属性 put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000) } return contentResolver.insert( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, videoDetails ) }
步骤2:启动相机Intent并传入自定义Uri
接下来,调用createCustomVideoUri生成你想要的Uri,然后传给MediaStore.EXTRA_OUTPUT,再启动相机Intent:
// 定义你想要的预定义文件名,比如加时间戳保证唯一性 val customFileName = "my_recording_${System.currentTimeMillis()}.mp4" val videoUri = createCustomVideoUri(customFileName) ?: run { // 创建Uri失败的错误处理 Toast.makeText(this, "无法创建视频存储路径", Toast.LENGTH_SHORT).show() return } val intent = Intent(MediaStore.ACTION_VIDEO_CAPTURE).apply { // 设置录制时长限制 putExtra("android.intent.extra.durationLimit", 5) // 传入自定义的Uri putExtra(MediaStore.EXTRA_OUTPUT, videoUri) // 可选:设置视频质量,1为高质量,0为低质量 putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1) } // 检查是否有相机应用可以处理该Intent if (intent.resolveActivity(packageManager) != null) { // 旧版API用startActivityForResult startActivityForResult(intent, 222) // 推荐迁移到Activity Result API,代码更安全: // videoCaptureLauncher.launch(intent) } else { Toast.makeText(this, "未找到可用的相机应用", Toast.LENGTH_SHORT).show() }
步骤3:处理录制结果
如果用的是旧版startActivityForResult,在回调中判断录制状态:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == 222) { if (resultCode == RESULT_OK) { Toast.makeText(this, "视频录制成功", Toast.LENGTH_SHORT).show() // 后续可以用videoUri处理视频,比如播放、编辑 } else if (resultCode == RESULT_CANCELED) { Toast.makeText(this, "录制已取消", Toast.LENGTH_SHORT).show() // 用户取消时,删除提前创建的空文件 contentResolver.delete(videoUri, null, null) } } }
如果是用Activity Result API(推荐),代码如下:
private val videoCaptureLauncher = registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { result -> if (result.resultCode == RESULT_OK) { Toast.makeText(this, "视频录制成功", Toast.LENGTH_SHORT).show() } else if (result.resultCode == RESULT_CANCELED) { Toast.makeText(this, "录制已取消", Toast.LENGTH_SHORT).show() contentResolver.delete(videoUri, null, null) } }
关键注意事项
- 权限要求:不需要
WRITE_EXTERNAL_STORAGE,但必须申请CAMERA权限(如果录制带音频,还需要RECORD_AUDIO权限)。Android 13+中这些都是危险权限,需要动态申请。 - 空文件清理:用户取消录制时,提前创建的空文件会留在存储中,记得在回调中删除。
- 文件名唯一性:尽量通过时间戳、随机字符串保证文件名唯一,避免覆盖已存在的文件。
- 存储位置:示例用的是
DCIM/MyVideos,你也可以修改RELATIVE_PATH使用其他公共目录,比如Environment.DIRECTORY_MOVIES。
内容的提问来源于stack exchange,提问作者Emrick Liu
相关产品推荐
相关产品推荐

