Xamarin Android 如何调用系统文件管理器打开共享存储指定文件夹
你当前的实现方向是对的,出现的问题都是配置和代码细节错误,按以下步骤修复即可覆盖API 21-30的需求:
第一步 修复provider_paths资源找不到报错
- 确认
provider_paths.xml存放路径:在你的Xamarin.Android项目的Resources/xml目录下,若xml文件夹不存在请手动创建 - 右键点击
provider_paths.xml选择【属性】,将【生成操作】设置为AndroidResource,【自定义工具】项留空 - 确认文件名全小写、无拼写错误,Android资源对大小写敏感,任何拼写或大小写错误都会导致资源找不到
第二步 修正FileProvider权限配置不匹配问题
你当前代码中FileProvider.GetUriForFile传入的授权名是Application.Context.PackageName + ".fileprovider",但Manifest中配置的android:authorities值为com.vtbuild17.demoapp.provider,两者必须完全一致才能正常工作,推荐按如下方式修正:
修改Manifest中provider节点的authorities属性,使用包名占位符避免后续包名变更导致不匹配:
<provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" /> </provider>
provider_paths.xml现有内容无需修改,已经可以覆盖公共存储目录的访问需求。
第三步 修正打开文件夹的业务代码
原有代码存在两个问题:一是两次调用SetFlags会覆盖之前的Flag配置,二是resource/folder不是通用MIME类型,多数第三方文件管理器不识别,修正后代码如下:
// 先确保已经获取读写外部存储的运行时权限,API 23+需要动态申请 string location = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDocuments).AbsolutePath; if (location.Contains("/Android")) { location = location.Split("/Android")[0]; } location += "/Exports container"; if (!Directory.Exists(location)) { Directory.CreateDirectory(location); } Intent intent = new Intent(Intent.ActionView); Java.IO.File folder = new Java.IO.File(location); var contentURI = FileProvider.GetUriForFile(Application.Context, Application.Context.PackageName + ".fileprovider", folder); // 适配不同文件管理器的MIME类型 intent.SetDataAndType(contentURI, DocumentsContract.Document.MimeTypeDir); // 叠加Flag,不要用SetFlags覆盖 intent.AddFlags(ActivityFlags.NewTask); intent.AddFlags(ActivityFlags.GrantReadUriPermission); intent.AddFlags(ActivityFlags.GrantWriteUriPermission); // 先判断是否有可处理该Intent的应用,避免崩溃 if (intent.ResolveActivity(PackageManager) != null) { StartActivity(intent); } else { // 兜底方案:调用通用文件浏览Intent Intent fallbackIntent = new Intent(Intent.ActionGetContent); fallbackIntent.SetDataAndType(contentURI, "*/*"); fallbackIntent.AddFlags(ActivityFlags.GrantReadUriPermission); if (fallbackIntent.ResolveActivity(PackageManager) != null) { StartActivity(fallbackIntent); } else { Toast.MakeText(Application.Context, "未找到可用的文件管理器", ToastLength.Long).Show(); } }
补充适配注意事项
- API 23及以上版本需要先动态申请
READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限,权限获取成功后再调用上述打开文件夹的逻辑 - 若后续升级到API 31及以上,只需将目标SDK版本对应升级,现有逻辑无需修改即可兼容
- 部分国产ROM对文件管理器Intent做了定制,若出现无法定位到指定文件夹的情况,可以在生成路径时避免路径中包含空格,比如将
Exports container改为ExportsContainer,兼容性更好
内容的提问来源于stack exchange,提问作者Alex A.
相关产品推荐
相关产品推荐

