targetSdkVersion 30下如何实现Android全机型兼容的视频图片选择功能
现有实现缺陷说明
- Intent配置逻辑错误:同时设置
ACTION_PICK和ACTION_GET_CONTENT两个冲突的Action,且为ACTION_GET_CONTENT传入了仅对拍照/录屏等输出类Action生效的MediaStore.EXTRA_OUTPUT参数,该参数对选择已有媒体的请求无意义,会导致不同厂商的系统选择器响应逻辑不一致。 - 权限逻辑冗余且不适配高版本系统:targetSdk≥33后
READ_EXTERNAL_STORAGE已完全废弃,且使用系统原生媒体/文件选择器时,无需申请任何存储权限,系统会为选中内容的URI自动授予临时读取权限,权限申请逻辑多余且会在高版本设备上引发授权异常。 - URI转本地路径的逻辑完全不符合高版本系统规范:Android 10引入分区存储后,应用无法直接访问其他应用/公共目录下的媒体文件路径,你所使用的
PathUtils依赖的_data系统数据库列已被官方废弃,返回的路径本身不具备访问权限,是引发崩溃的核心原因。 - 临时授权丢失:系统选择器返回的URI仅对当前请求的Activity授予临时读取权限,你仅传递转换后的文件路径到下一个Activity,既丢失了URI本身的临时授权,也没有任何机制让下一个Activity获得文件访问权限,三星等厂商的系统权限管控更严格,因此会触发权限错误。
- 使用已废弃的API:
startActivityForResult已在AndroidX Activity 1.2.0、Fragment 1.3.0版本被官方正式废弃,存在兼容性隐患。
官方推荐标准实现方案
所有实现逻辑均有Android官方文档/API规范支撑,全机型适配无权限问题,无需引入任何第三方库。
1. 替换请求逻辑为官方Activity Result API
首先注册媒体选择的结果回调,以选择单视频为例:
// 注册选择视频的结果回调 private val pickVideoLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { selectedUri: Uri? -> selectedUri ?: return@registerForActivityResult // 直接传递URI到下一个Activity,同时传递读取权限 val nextIntent = Intent(this, VideoEditActivity::class.java).apply { data = selectedUri addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) } startActivity(nextIntent) }
触发媒体选择的逻辑,无需申请任何存储权限:
// 直接拉起系统选择器,不需要申请存储权限 pickVideoLauncher.launch("video/*")
如果需要同时选择图片和视频,可使用官方统一媒体选择器:
// 注册选图/选视频回调 private val pickMediaLauncher = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { selectedUri: Uri? -> // 处理逻辑同上 } // 触发选择,可指定仅图片、仅视频、图片+视频 pickMediaLauncher.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageAndVideo))
2. 修改下一个页面的媒体加载逻辑
不要使用路径加载,直接用URI作为数据源,MediaPlayer原生支持该加载方式:
override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) { Log.d("VIDEOPLAYER", "SURFACE") mediaPlayer.setSurface(Surface(surface)) mediaPlayer.isLooping = true // 直接传入上下文和URI设置数据源,不需要文件路径 mediaPlayer.setDataSource(this, intent.data!!) // 后续正常执行prepare、start逻辑即可 }
核心实现依据说明
- 系统媒体选择器返回的URI会自动授予临时读取权限,有效期直到应用进程退出,传递给其他组件时仅需添加
FLAG_GRANT_READ_URI_PERMISSION即可延续授权,无需申请任何存储权限。 - Android 10及以上版本官方明确禁止直接访问公共媒体文件的本地路径,所有访问必须通过URI完成,
MediaPlayer原生提供的setDataSource(Context, Uri)重载会自动处理URI的权限校验和资源加载。 - Activity Result API是官方指定替代
startActivityForResult的标准实现,不存在兼容性问题。
内容的提问来源于stack exchange,提问作者Benjamin Basmaci
相关产品推荐
相关产品推荐

