Cordova Android插件:USB存储指定扩展名最新文件的权限与实现问题
解决Android 11+下USB存储文件访问权限问题及文件列表实现
一、权限处理(Android 11+)
- MANAGE_EXTERNAL_STORAGE权限的局限性:Android 11及以上版本中,即便申请了该权限,访问USB OTG存储仍可能受限——USB存储属于可移动媒体,需要额外适配处理。
- 正确的权限方案:
- 声明权限:在
AndroidManifest.xml中添加对应权限<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:minSdkVersion="30" /> <!-- Android 13+ 媒体文件权限,非媒体文件仍依赖MANAGE_EXTERNAL_STORAGE --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" android:minSdkVersion="33" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" android:minSdkVersion="33" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" android:minSdkVersion="33" /> - 动态申请权限:
- Android 10及以下版本,直接申请
READ_EXTERNAL_STORAGE - Android 11-12版本,申请
MANAGE_EXTERNAL_STORAGE并引导用户到系统设置开启:if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (!Environment.isExternalStorageManager()) { Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); Uri uri = Uri.fromParts("package", getContext().getPackageName(), null); intent.setData(uri); startActivityForResult(intent, REQUEST_CODE_MANAGE_STORAGE); } } else { ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_CODE_READ_STORAGE); }
- Android 10及以下版本,直接申请
- USB存储的合规访问方案:使用Storage Access Framework (SAF) 让用户手动选择USB存储目录,无需依赖
MANAGE_EXTERNAL_STORAGE,更符合隐私规范:
在Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); startActivityForResult(intent, REQUEST_CODE_PICK_USB_DIR);onActivityResult中获取授权Uri后,可通过DocumentFile操作文件。
- 声明权限:在
二、文件列表功能优化
针对你的需求,优化后的函数直接返回指定扩展名的最新文件,同时增强错误处理:
private File getLatestFileWithExtension(File directory, final String extension) { if (directory == null || !directory.exists() || !directory.canRead()) { Log.e("USBManager", "目录无效或无读取权限"); return null; } try { return Files.walk(directory.toPath(), 1) .filter(p -> { try { // 大小写不敏感匹配扩展名,避免漏检 String fileName = p.getFileName().toString().toLowerCase(); return Files.isRegularFile(p) && Files.isReadable(p) && fileName.endsWith(extension.toLowerCase()); } catch (Exception e) { Log.w("USBManager", "跳过无法访问的文件: " + p.getFileName(), e); return false; } }) // 按修改时间倒序排序,取第一个即为最新文件 .sorted((p1, p2) -> { try { return Long.compare(Files.getLastModifiedTime(p2).toMillis(), Files.getLastModifiedTime(p1).toMillis()); } catch (IOException e) { return 0; } }) .map(Path::toFile) .findFirst() .orElse(null); } catch (IOException e) { Log.e("USBManager", "获取文件列表失败: " + e.getMessage(), e); return null; } }
关键优化点
- 直接返回最新单个文件,无需额外处理列表
- 增加目录合法性前置检查,提前输出错误日志
- 扩展名匹配支持大小写不敏感,提升兼容性
- 对无法访问的文件打印警告,便于排查问题
- 使用
findFirst()替代全列表收集,优化性能
三、Cordova插件适配注意事项
- 借助
cordova-plugin-android-permissions插件处理动态权限申请逻辑 - SAF跳转的结果需在Cordova的Activity中处理
onActivityResult,并将结果回调到JavaScript层
内容的提问来源于stack exchange,提问作者Majkl Dovi
相关产品推荐
相关产品推荐

