Flutter中Android API30+权限拒绝异常的正确处理方法
解决Flutter在API30+设备上遍历目录时的权限异常问题
首先,你遇到的核心问题是异常抛出的位置不在你包裹try-catch的回调里,所以你的try-catch根本没生效。让我拆解下原因和解决方案:
为什么你的try-catch没捕获到异常?
Directory.list(recursive: true)返回的是一个Stream<FileSystemEntity>,当它递归遍历到没有权限的目录(比如Android/data或obb)时,异常是从Stream内部抛出的,而不是在listen的onData回调里。你把try-catch放在回调函数里,自然抓不到这个层级的异常。
正确的异常捕获方式
你需要通过Stream的错误处理机制来捕获这些异常,有两种常用方式:
方式1:使用handleError方法
在Stream链上添加handleError来处理遍历过程中抛出的异常:
Directory(_storageInstance.rootDirectory) .list(recursive: true) .handleError((error) { if (error is FileSystemException) { print("无法访问目录: ${error.path},错误: ${error.message}"); } }) .listen((event) { _storageInstance.searchList.add(SearchEntity( path: event.path, type: event.runtimeType.toString() == "_Directory" ? EntityType.DIRECTORY : EntityType.FILE)); });
方式2:在listen中添加onError回调
直接在listen方法里指定错误处理逻辑:
Directory(_storageInstance.rootDirectory) .list(recursive: true) .listen( (event) { _storageInstance.searchList.add(SearchEntity( path: event.path, type: event.runtimeType.toString() == "_Directory" ? EntityType.DIRECTORY : EntityType.FILE)); }, onError: (error) { if (error is FileSystemException) { print("权限异常: ${error.path},错误码: ${error.osError?.errorCode}"); } }, );
针对API30+的存储权限补充说明
Android 11(API30)及以上对外部存储做了严格限制:
- 即使你申请了
READ_EXTERNAL_STORAGE权限,也无法访问其他应用的Android/data和obb目录,这是系统的强制限制。 - 如果你的需求是访问自己应用的
data或obb目录,不要用根目录遍历,直接使用Flutter提供的路径API:// 获取自己应用的文档目录 final appDocDir = await getApplicationDocumentsDirectory(); // 获取自己应用的外部存储目录(含obb相关路径) final appExternalDir = await getExternalStorageDirectory(); - 如果确实需要访问整个外部存储(包括其他应用的目录),你需要申请
MANAGE_EXTERNAL_STORAGE权限,这个权限需要用户手动在设置中开启,而且Google Play对使用这个权限的应用审核非常严格,只有特定类型的应用(如文件管理器)才会通过审核。申请方式如下:- 在
AndroidManifest.xml中添加权限:<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> - 在Flutter中请求权限:
import 'package:permission_handler/permission_handler.dart'; Future<void> requestStoragePermission() async { if (await Permission.manageExternalStorage.isDenied) { await Permission.manageExternalStorage.request(); } }
- 在
优化建议:避免无意义的递归遍历
为了减少不必要的异常,你可以在遍历前判断当前目录是否是受限制的Android/data或obb,直接跳过这些目录:
Directory(_storageInstance.rootDirectory) .list(recursive: false) .listen((event) async { // 跳过系统限制的目录 if (event.path.endsWith("/Android/data") || event.path.endsWith("/Android/obb")) { return; } // 如果是目录,递归处理子内容 if (event is Directory) { event.list(recursive: true).listen((subEvent) { _storageInstance.searchList.add(SearchEntity( path: subEvent.path, type: subEvent is Directory ? EntityType.DIRECTORY : EntityType.FILE)); }).onError((error) { print("子目录遍历异常: ${error.toString()}"); }); } else { _storageInstance.searchList.add(SearchEntity( path: event.path, type: EntityType.FILE)); } });
这样可以从源头避免进入受限制的目录,减少异常的发生。
内容的提问来源于stack exchange,提问作者Sarvesh Dalvi
相关产品推荐
相关产品推荐

