一、背景
在移动应用开发中,访问用户设备上的照片和媒体库是一项常见的需求,尤其是在社交媒体、摄影类应用中。然而,传统上实现这一功能通常需要申请读取存储权限,用户在主动授权后,应用可以使用存储访问API读取设备上的照片和媒体数据。这种方案在实践中也存在诸多痛点,具体体现在以下几个方面:
- 全面访问权限的风险:应用在获得读取存储权限后,可以访问用户设备上所有的照片和媒体文件。而实际上,应用可能仅需要用户选择的特定照片。这种全面访问的能力可能会引发用户和监管机构对隐私泄露的担忧,从而增加应用在隐私保护方面的合规负担。
- 用户体验的挑战:出于对隐私的考量,用户可能会拒绝授予存储访问权限,从而导致应用中某些需要访问照片的功能无法使用。这种情况不仅影响用户体验,还可能对应用的功能完整性造成影响。
- 授权操作的复杂性:当用户多次误拒绝权限请求,但仍希望使用相应功能时,应用需要引导用户手动前往系统设置中授予权限,这一过程繁琐且易使用户流失。
为解决上述问题,PhotoPicker(照片视频选择器)应运而生。通过集成PhotoPicker,应用可以弹出系统级的照片视频选择界面,用户在界面中手动选择并授权应用读取特定的照片视频。这一机制提供了以下显著优势:
- 无需存储权限:应用不再需要申请全面的存储读取权限,避免了因用户拒绝授权而导致的功能不可用问题。
- 最小化信息访问:应用仅能读取用户明确选择的照片视频数据,符合最小化数据访问的原则,降低隐私风险和合规压力。
- 增强用户信任:通过透明化的照片视频选择和授权流程,增强用户对应用的信任感,提高应用的用户体验和使用满意度。
通过引入PhotoPicker,开发者不仅能提升应用的隐私保护标准,还能优化用户交互体验,使应用在日益严格的隐私监管环境中具备更强的竞争力。
二、功能介绍
PhotoPicker 提供系统级照片视频选择功能,允许用户在应用内通过一个统一界面选择并授权共享特定照片视频,应用随后能读取这些用户选中的照片视频数据。

三、 适用范围
当前支持PhotoPicker的操作系统版本如下:
- ColorOS 16(基于Android 16)及以上
- HyperOS 3(基于Android 16)及以上
- OriginOS 6(基于Android 16)及以上
- MagicOS 10(基于Android 16)及以上
四、使用说明
配置Action即可调用PhotoPicker,其余参数按需配置,在onActivityResult对返回数据进行处理。
4.1 Action
MediaStore.ACTION_PICK_IMAGES
该action仅使用于图片/视频,请求方能读取选中图片/视频的数据,无需申请读权限。
返回picker uri,该uri类型仅支持只读、获取有限列信息MediaStore.PickerMediaColumns,其中DATA字段被修饰,非文件在公共路径下的实际路径,ex/sdcard/.transforms/synthetic/picker/0/com.android.providers.media.photopicker/media/xx
参数说明:
| 名称 | 值 | 必填 | 说明 |
| Action | MediaStore.ACTION_PICK_IMAGES | 是 | 允许用户通过该Action获取到图片/视频数据 |
代码配置:
Java:
Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
startActivityForResult(intent, CODE_PICK_IMAGES);Kotlin:
val intent = Intent(MediaStore.ACTION_PICK_IMAGES)
startActivityForResult(intent, CODE_PICK_IMAGES)4.2 Type
获取数据范围
配置值:
| 名称 | 数据类型 | 值 | 说明 |
| mimeType | String | image/* | 只获取图片 |
| video/* | 只获取视频 | ||
| / | 不配置 | 图片和视频都获取 |
是否必填: 否
代码配置:
Java:
intent.setType("image/*")//仅图片
intent.setType("video/*")//仅视频Kotlin:
intent.type = "image/*" // 仅图片
intent.type = "video/*" // 仅视频若不设置type,图片和视频都显示
4.3 Extra
4.3.1 多选模式最大选择数
配置参数: MediaStore.EXTRA_PICK_IMAGES_MAX
系统值: android.provider.extra.PICK_IMAGES_MAX
是否必填: 否
参数说明:
| 参数 | 参数类型 | 参数说明 |
| MediaStore.EXTRA_ PICK_IMAGES_MAX | int | 设置多选模式,值大于1且小于等于100。不传该参数为单选模式 |
代码配置:
Java:
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, 50);Kotlin:
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, 50)4.3.2 预选设置
配置参数: MediaStore.EXTRA_PICKER_PRE_SELECTION_URIS
系统值: android.provider.extra.PICKER_PRE_SELECTION_URIS
| 参数 | 数据类型 | 参数说明 |
| MediaStore.EXTRA_PICKER_PRE_SELECTION_URIS | ArrayList<Uri> | 多选拉起PhotoPicker时,设置预选图片/视频。受MediaStore.EXTRA_PICK_IMAGES_MAX影响,集合长度不可以超过设置的最大选择数 |
是否必填: 否
代码配置:
intent.putExtra(MediaStore.EXTRA_PICKER_PRE_SELECTION_URIS, preSelectResultUris); 五、使用示例
Java:
Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); // 必配
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, 50); // 选配
intent.setType("image/*"); // 选配
intent.putExtra(MediaStore. EXTRA_PICK_IMAGES_IN_ORDER, false); // 选配
intent.putExtra(MediaStore.EXTRA_PICKER_PRE_SELECTION_URIS, preSelectResultUris); // 选配
startActivityForResult(intent, CODE_PICK_IMAGES); Kotlin:
val intent = Intent(MediaStore.ACTION_PICK_IMAGES).apply {
putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, 50)// 选配
type = "image/*" // 选配
putExtra(MediaStore.EXTRA_PICK_IMAGES_IN_ORDER, false) // 选配
putExtra(MediaStore.EXTRA_PICKER_PRE_SELECTION_URIS, preSelectResultUris) // 选配
}
startActivityForResult(intent, CODE_PICK_IMAGES)输出结果获取在onActivityResult中获取选中图片的uri集合,区分单选和多选
单选:从intent中直接获取:data.getData()
多选:从intent中取ClipData,uri信息封装在ClipData中
Java:
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CODE_PICK_IMAGE && resultCode == Activity.RESULT_OK) {
if (data != null) {
mSelectResultUris.clear();
if (mIsMultiSelect) { // 多选,设置多选参数时配置
ClipData clipData = data.getClipData();
if (clipData != null && clipData.getItemCount() > 0) {
for (int i = 0; i < clipData.getItemCount(); i++) {
ClipData.Item item = clipData.getItemAt(i);
mSelectResultUris.add(item.getUri());
}
}
} else { // 单选
mSelectResultUris.add(data.getData());
}
Log.d(TAG, "onActsivityResult: mSelectResultUri = " + mSelectResultUris.size());
}
}
}Kotlin:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == CODE_PICK_IMAGES && resultCode == Activity.RESULT_OK) {
data?.let {
mSelectResultUris.clear()
// 多选情况
val clipData = it.clipData
if (clipData != null && clipData.itemCount > 0) {
for (i in 0 until clipData.itemCount) {
val item = clipData.getItemAt(i)
mSelectResultUris.add(item.uri)
}
} else {
// 单选情况
it.data?.let { uri ->
mSelectResultUris.add(uri)
}
}
Log.d(TAG, "onActivityResult: 选择的图片数量 = ${mSelectResultUris.size}")
mSelectResultUris.forEachIndexed { index, uri ->
Log.d(TAG, "图片 $index: $uri")
}
}
}
}该适配指南与金标联盟官网适配指南一致,您也可查阅金标联盟官网中公示适配指南。
金标联盟官方文档地址:https://www.itgsa.com/doc/6631953378231296