Android 10相机视频权限实战华为Mate30深度解决方案最近在华为Mate30上调试相机视频功能时不少开发者反馈遇到EACCES (Permission denied)错误。这个问题看似简单实则涉及Android 10存储权限机制的深层变革。本文将带您彻底解决这个痛点问题。1. 问题根源与背景解析当你在华为Mate30或其他Android 10设备上尝试访问/storage/emulated/0/DCIM/Camera/xx.mp4时系统抛出FileNotFoundException并提示权限拒绝。这不是代码写错了而是Android 10引入的**分区存储(Scoped Storage)**机制在作祟。传统Android存储方式就像开放式图书馆应用可以随意翻阅任何书架。而Android 10则给每个应用发了专属借书卡媒体文件通过MediaStore API访问下载目录使用Storage Access Framework应用私有目录直接文件操作关键变化在于// 旧方式Android 9及以下 File videoFile new File(/storage/emulated/0/DCIM/Camera/video.mp4); // 新方式Android 10 ContentResolver resolver getContentResolver(); Uri videoUri MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);2. 华为Mate30特殊适配方案针对华为设备我们需要双管齐下2.1 AndroidManifest关键配置manifest !-- 基础权限 -- uses-permission android:nameandroid.permission.READ_EXTERNAL_STORAGE/ uses-permission android:nameandroid.permission.WRITE_EXTERNAL_STORAGE/ application android:requestLegacyExternalStoragetrue ... !-- FileProvider配置 -- provider android:nameandroidx.core.content.FileProvider android:authorities${applicationId}.fileprovider android:exportedfalse android:grantUriPermissionstrue meta-data android:nameandroid.support.FILE_PROVIDER_PATHS android:resourcexml/file_paths/ /provider /application /manifest注意requestLegacyExternalStorage只是临时方案Android 11设备需要逐步迁移到分区存储2.2 FileProvider路径配置在res/xml/file_paths.xml中?xml version1.0 encodingutf-8? paths xmlns:androidhttp://schemas.android.com/apk/res/android external-path nameexternal_storage_root path./ external-files-path nameexternal_files path./ external-cache-path nameexternal_cache path./ root-path nameroot path./ /paths路径类型对照表元素对应路径等效Java方法files-path/data/data/[package]/filesContext.getFilesDir()cache-path/data/data/[package]/cacheContext.getCacheDir()external-path/storage/emulated/0Environment.getExternalStorageDirectory()external-files-pathAndroid/data/[package]/filesContext.getExternalFilesDir()external-cache-pathAndroid/data/[package]/cacheContext.getExternalCacheDir()3. 现代存储API实战3.1 通过MediaStore查询视频fun queryVideos(context: Context): ListVideo { val videos mutableListOfVideo() val projection arrayOf( MediaStore.Video.Media._ID, MediaStore.Video.Media.DISPLAY_NAME, MediaStore.Video.Media.DATE_TAKEN ) val sortOrder ${MediaStore.Video.Media.DATE_TAKEN} DESC context.contentResolver.query( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projection, null, null, sortOrder )?.use { cursor - while (cursor.moveToNext()) { val id cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID)) val name cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME)) val date cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATE_TAKEN)) val contentUri ContentUris.withAppendedId( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id ) videos.add(Video(contentUri, name, date)) } } return videos }3.2 安全访问视频文件获取到Uri后实际使用时// 读取视频 try (InputStream in getContentResolver().openInputStream(videoUri)) { // 处理视频流 } // 分享视频 val shareIntent Intent().apply { action Intent.ACTION_SEND putExtra(Intent.EXTRA_STREAM, videoUri) type video/* addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) } startActivity(Intent.createChooser(shareIntent, 分享视频))4. 常见问题排查指南遇到EACCES错误时按以下步骤检查权限验证检查是否已动态申请READ_EXTERNAL_STORAGE华为设备需额外确认没有启用增强保护模式路径验证adb shell ls -l /storage/emulated/0/DCIM/Camera确认文件确实存在且权限正确FileProvider配置检查authorities是否与manifest声明一致路径xml是否包含目标目录特殊场景处理相机拍摄后立即访问使用FileProvider.getUriForFile()第三方应用创建的文件通过MediaScannerConnection扫描华为设备特有注意点EMUI可能修改默认存储路径某些系统版本会限制后台访问开发者选项中关闭存储空间隔离进行测试5. 未来兼容性建议虽然requestLegacyExternalStorage能暂时解决问题但建议逐步迁移关键数据存储到应用私有目录媒体文件通过MediaStore API访问用户选择文件使用Storage Access Framework示例使用SAF选择视频val intent Intent(Intent.ACTION_OPEN_DOCUMENT).apply { addCategory(Intent.CATEGORY_OPENABLE) type video/* addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) } startActivityForResult(intent, REQUEST_VIDEO_PICK)在华为Mate30上测试时发现某些EMUI版本需要额外处理Uri权限持久化。这提醒我们在Android生态中既要遵循标准方案也要为厂商定制留出适配空间。
Android10相机视频访问权限避坑指南:华为Mate30实测解决方案
Android 10相机视频权限实战华为Mate30深度解决方案最近在华为Mate30上调试相机视频功能时不少开发者反馈遇到EACCES (Permission denied)错误。这个问题看似简单实则涉及Android 10存储权限机制的深层变革。本文将带您彻底解决这个痛点问题。1. 问题根源与背景解析当你在华为Mate30或其他Android 10设备上尝试访问/storage/emulated/0/DCIM/Camera/xx.mp4时系统抛出FileNotFoundException并提示权限拒绝。这不是代码写错了而是Android 10引入的**分区存储(Scoped Storage)**机制在作祟。传统Android存储方式就像开放式图书馆应用可以随意翻阅任何书架。而Android 10则给每个应用发了专属借书卡媒体文件通过MediaStore API访问下载目录使用Storage Access Framework应用私有目录直接文件操作关键变化在于// 旧方式Android 9及以下 File videoFile new File(/storage/emulated/0/DCIM/Camera/video.mp4); // 新方式Android 10 ContentResolver resolver getContentResolver(); Uri videoUri MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);2. 华为Mate30特殊适配方案针对华为设备我们需要双管齐下2.1 AndroidManifest关键配置manifest !-- 基础权限 -- uses-permission android:nameandroid.permission.READ_EXTERNAL_STORAGE/ uses-permission android:nameandroid.permission.WRITE_EXTERNAL_STORAGE/ application android:requestLegacyExternalStoragetrue ... !-- FileProvider配置 -- provider android:nameandroidx.core.content.FileProvider android:authorities${applicationId}.fileprovider android:exportedfalse android:grantUriPermissionstrue meta-data android:nameandroid.support.FILE_PROVIDER_PATHS android:resourcexml/file_paths/ /provider /application /manifest注意requestLegacyExternalStorage只是临时方案Android 11设备需要逐步迁移到分区存储2.2 FileProvider路径配置在res/xml/file_paths.xml中?xml version1.0 encodingutf-8? paths xmlns:androidhttp://schemas.android.com/apk/res/android external-path nameexternal_storage_root path./ external-files-path nameexternal_files path./ external-cache-path nameexternal_cache path./ root-path nameroot path./ /paths路径类型对照表元素对应路径等效Java方法files-path/data/data/[package]/filesContext.getFilesDir()cache-path/data/data/[package]/cacheContext.getCacheDir()external-path/storage/emulated/0Environment.getExternalStorageDirectory()external-files-pathAndroid/data/[package]/filesContext.getExternalFilesDir()external-cache-pathAndroid/data/[package]/cacheContext.getExternalCacheDir()3. 现代存储API实战3.1 通过MediaStore查询视频fun queryVideos(context: Context): ListVideo { val videos mutableListOfVideo() val projection arrayOf( MediaStore.Video.Media._ID, MediaStore.Video.Media.DISPLAY_NAME, MediaStore.Video.Media.DATE_TAKEN ) val sortOrder ${MediaStore.Video.Media.DATE_TAKEN} DESC context.contentResolver.query( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projection, null, null, sortOrder )?.use { cursor - while (cursor.moveToNext()) { val id cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID)) val name cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME)) val date cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATE_TAKEN)) val contentUri ContentUris.withAppendedId( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id ) videos.add(Video(contentUri, name, date)) } } return videos }3.2 安全访问视频文件获取到Uri后实际使用时// 读取视频 try (InputStream in getContentResolver().openInputStream(videoUri)) { // 处理视频流 } // 分享视频 val shareIntent Intent().apply { action Intent.ACTION_SEND putExtra(Intent.EXTRA_STREAM, videoUri) type video/* addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) } startActivity(Intent.createChooser(shareIntent, 分享视频))4. 常见问题排查指南遇到EACCES错误时按以下步骤检查权限验证检查是否已动态申请READ_EXTERNAL_STORAGE华为设备需额外确认没有启用增强保护模式路径验证adb shell ls -l /storage/emulated/0/DCIM/Camera确认文件确实存在且权限正确FileProvider配置检查authorities是否与manifest声明一致路径xml是否包含目标目录特殊场景处理相机拍摄后立即访问使用FileProvider.getUriForFile()第三方应用创建的文件通过MediaScannerConnection扫描华为设备特有注意点EMUI可能修改默认存储路径某些系统版本会限制后台访问开发者选项中关闭存储空间隔离进行测试5. 未来兼容性建议虽然requestLegacyExternalStorage能暂时解决问题但建议逐步迁移关键数据存储到应用私有目录媒体文件通过MediaStore API访问用户选择文件使用Storage Access Framework示例使用SAF选择视频val intent Intent(Intent.ACTION_OPEN_DOCUMENT).apply { addCategory(Intent.CATEGORY_OPENABLE) type video/* addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) } startActivityForResult(intent, REQUEST_VIDEO_PICK)在华为Mate30上测试时发现某些EMUI版本需要额外处理Uri权限持久化。这提醒我们在Android生态中既要遵循标准方案也要为厂商定制留出适配空间。