Photo Library Permission — 是应用程序访问用户设备上存储的照片和视频所需的权限。它用于从图库中选择图片、保存媒体文件和管理相册。根据 Apple Developer Documentation, 2024,从 iOS 14 开始,应用程序必须请求访问受限的相册,而不是一次性访问所有照片。
要点
Photo Library Permission — 是管理系统应用程序访问设备上存储的媒体文件的权限。它涵盖读取(查看和选择照片和视频)和写入(将新文件保存到图库)。移动平台将媒体库视为机密数据,因为照片可能包含个人和敏感信息。
在 Android 13(API 33)上,Google 将单一的 READ_EXTERNAL_STORAGE 权限分为三个独立的权限:READ_MEDIA_IMAGES 用于照片,READ_MEDIA_VIDEO 用于视频,READ_MEDIA_AUDIO 用于音频。这允许用户仅授予对特定类型媒体文件的访问权限。在 iOS 上,从 iOS 14 开始,PhotoKit 允许用户选择特定的照片进行访问,而不是打开整个图库。
根据 Counterpoint Research (2024) 的数据,在社交网络、通讯和照片编辑类别中,超过 70% 的移动应用程序请求 Photo Library Permission。同时,如果用户不理解为什么应用程序需要他们的照片,32% 的用户会拒绝访问图库。Apple 和 Google 建议使用系统 PhotoPicker 来选择单个文件,而无需请求完全访问权限。
Android 上的图库访问 随着 Android 13 的发布发生了显著变化。不再是单一的 READ_EXTERNAL_STORAGE 权限,而是针对每种媒体文件类型出现了细粒度的权限,给用户提供了更多的控制。
从 Android 13 开始,访问照片使用 READ_MEDIA_IMAGES,访问视频使用 READ_MEDIA_VIDEO。READ_EXTERNAL_STORAGE 权限不再提供对媒体文件的访问 — 它仅保留用于从共享存储中读取非媒体文件。清单中的声明如下所示:
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
为了向后兼容 Android 12 及更低版本,还需要声明 READ_EXTERNAL_STORAGE。然而,当存在新的细粒度权限时,Android 13 会自动忽略此权限。在清单中对旧权限使用 android:maxSdkVersion="32" 属性,以在新版本上隐藏它。这可以防止在 Android 13+ 设备上出现不必要的请求。
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
Android 上 Photo Library Permission 的运行时请求通过 Activity Result API 执行。请求访问图像的示例:
private val photoPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
if (granted) {
loadGalleryImages()
} else {
showPermissionDenied()
}
}
fun pickPhoto() {
val permission = if (Build.version.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
if (ContextCompat.checkSelfPermission(this, permission)
== PackageManager.PERMISSION_GRANTED) {
loadGalleryImages()
} else {
photoPermissionLauncher.launch(permission)
}
}
iOS 上的图库访问 通过 PhotoKit 框架和 Info.plist 中的 NSPhotoLibraryUsageDescription 及 NSPhotoLibraryAddUsageDescription 键进行管理。从 iOS 14 开始,用户可以只允许访问选定的照片(Limited Photo Library)。
从图库读取照片需要 NSPhotoLibraryUsageDescription,写入(保存照片)需要 NSPhotoLibraryAddUsageDescription。如果应用程序只保存媒体但不读取它们,第二个键就足够了。
<key>NSPhotoLibraryUsageDescription</key>
<string>应用程序需要访问照片以选择个人资料图片和发布照片。</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>应用程序需要访问权限以将处理后的照片保存到您的图库。</string>
iOS 上访问照片的请求通过 PHPhotoLibrary.requestAuthorization 执行。从 iOS 14 开始,.limited 状态表示用户已授予对有限照片集的访问权限。
import PhotosUI
func requestPhotoLibraryAccess() {
PHPhotoLibrary.requestAuthorization { status in
DispatchQueue.main.async {
switch status {
case .authorized:
self.loadPhotoLibrary()
case .limited:
self.loadSelectedPhotos()
case .denied, .restricted:
self.showSettingsAlert()
case .notDetermined:
break
}
}
}
}
private func loadPhotoLibrary() {
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let assets = PHAsset.fetchAssets(with: .image, options: fetchOptions)
// 处理请求结果
}
iOS 14+ 上的 .limited 状态意味着用户只允许访问几张选定的照片。应用程序可以通过系统对话框调用 PHPhotoLibrary.shared().presentLimitedLibraryPicker 来请求扩展访问权限。建议仅在用户明确请求需要额外照片的功能时才显示此对话框。
PhotoPicker — 是用于选择媒体文件的系统组件,不需要访问整个图库的权限。从 Android 13 和 iOS 14 开始,如果应用程序只需要选择一张或几张照片,Google 和 Apple 建议使用 PhotoPicker 而不是请求 Photo Library Permission。
在 Android 13+ 上,内置的 PhotoPicker(ActivityResultContracts.PickVisualMedia)允许用户选择照片或视频,而无需授予对整个图库的访问权限。应用程序仅接收所选文件的 URI。PhotoPicker 无需在清单中声明任何权限即可工作。
private val pickMedia =
registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
if (uri != null) {
// 所选文件的 URI,无需访问整个图库
showImage(uri)
}
}
fun selectPhoto() {
pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
}
在 iOS 14+ 上出现了 PHPickerViewController — 一个系统选择器,也不需要访问整个图库。PHPicker 在单独的进程中运行,无权访问应用程序的图库。这是大多数情况下选择照片的首选方法。
import PhotosUI
func presentPhotoPicker() {
var config = PHPickerConfiguration()
config.selectionLimit = 1
config.filter = .images
let picker = PHPickerViewController(configuration: config)
picker.delegate = self
present(picker, animated: true)
}
如果应用程序需要访问整个图库进行批量操作(备份、云同步、图库应用程序、文件管理器),PhotoPicker 则不合适。在这些情况下,需要请求 Photo Library Permission 并处理有限访问(Limited)的情况。对于 iOS,必须添加 .limited 状态的处理,并向用户提供扩展访问的选项。
最佳实践 是与 Photo Library Permission 配合使用,有助于减少拒绝次数并满足平台的隐私保护要求。
对于选择一张或多张照片,请使用系统 PhotoPicker 或 PHPicker。这些组件不需要权限,并提供最佳用户体验。仅在应用程序功能需要访问整个媒体库时才请求完全访问图库的权限。Google 和 Apple 在开发者文档中明确推荐这种方法。
如果应用程序确实需要完全访问图库,请显示一个带有解释的初始屏幕。解释为什么需要所有照片,而不仅仅是一张。例如,备份应用程序应该解释它将扫描所有媒体文件以创建副本。由于 Limited Access 机制,Pre-permission screen 在 iOS 上尤其重要。
如果应用程序只保存照片但不读取它们(例如,保存结果的照片编辑器),则只请求写入图库的权限。在 Android 上,这是 WRITE_EXTERNAL_STORAGE(适用于 Android 12 及更低版本)或无需权限的 MediaStore API(适用于 Android 10+)。在 iOS 上,使用 NSPhotoLibraryAddUsageDescription 而不使用 NSPhotoLibraryUsageDescription。
常见问题
从 Android 10(API 29)开始,通过 MediaStore 保存照片不需要 WRITE_EXTERNAL_STORAGE 权限。系统提供对共享存储的写入访问权限,无需显式权限。读取他人的文件仍然需要相应的权限。
READ_EXTERNAL_STORAGE — 从共享存储中读取所有文件的旧权限(直到 Android 12)。READ_MEDIA_IMAGES — 仅读取图像的新细粒度权限(Android 13+)。用户可以只授予对照片的访问权限,而拒绝访问视频和音频。
.limited 状态意味着用户选择了几张照片进行访问。应用程序可以通过 PHPhotoLibrary.shared().presentLimitedLibraryPicker 显示系统扩展对话框。建议仅在用户明确请求时显示此对话框,例如,点击「添加更多照片」按钮时。
可以,拥有 Photo Library Permission 后,应用程序可以读取 EXIF 数据,包括地理标签、拍摄日期和相机型号。在 iOS 上,访问地理标签可能需要额外的 Location Permission,因为 Apple 将 GPS 坐标视为机密数据。
使用系统选择器:iOS 上使用 filter = .videos 的 PHPicker,Android 上使用 VideoOnly 的 PickVisualMedia。在 Android 上,也可以使用带有 MediaStore.Video.Media.EXTERNAL_CONTENT_URI 的 Intent(Intent.ACTION_PICK) 从图库中选择视频,但这在 Android 13 以下的版本上需要 READ_EXTERNAL_STORAGE 权限。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。