Xamarin.Forms Android应用无法读取外部存储问题咨询
问题
我们开发的仅面向Android设备的Xamarin.Forms应用「myApp」,需要读写路径为
storage/emulated/0/Android/data/com.myApp.configuration/myAppStuff.txt
的文本文件,该路径在Windows 10开发环境中显示为
This PC\DEVICE TYPE\Internal shared storage\Android\data\com.myApp.configuration\
已在项目的AndroidManifest.xml中添加以下权限:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" ... > <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:remove="android:maxSdkVersion"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:remove="android:maxSdkVersion"/> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:remove="android:maxSdkVersion"/> </manifest>
且已在Visual Studio 2022中通过「Project > myApp.Android Properties > Android Manifest > Required permissions」确认权限已声明。
调试启动应用时,MainActivity.cs中
AndroidX.Core.App.ActivityCompat.CheckSelfPermission(this, Android.Manifest.Permission.ReadExternalStorage)
返回false,于是调用
AndroidX.Core.App.ActivityCompat.RequestPermissions(this, new string[] { Android.Manifest.Permission.ReadExternalStorage, Android.Manifest.Permission.WriteExternalStorage, Android.Manifest.Permission.ManageExternalStorage }, 0)
弹出权限申请对话框并选择「ALLOW」后,
File.Exists("/storage/emulated/0/Android/data/com.myApp.configuration/myAppStuff.txt")
返回true,但
mystreamreader = new StreamReader("/storage/emulated/0/Android/data/com.myApp.configuration/myAppStuff.txt");
抛出异常,提示:
"Access to the path \"/storage/emulated/0/Android/data/com.myApp.configuration/myAppStuff.txt\" is denied."
后续检查发现
AndroidX.Core.App.ActivityCompat.CheckSelfPermission(this, Android.Manifest.Permission.ManageExternalStorage)
返回false,请问这是什么原因?
原因分析与解决方案
核心原因:MANAGE_EXTERNAL_STORAGE权限的特殊性
Android 11(API 30)及以上版本中,MANAGE_EXTERNAL_STORAGE是特殊权限,无法通过常规的RequestPermissions弹窗申请:
- 常规的
READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE可以通过弹窗授权,但MANAGE_EXTERNAL_STORAGE属于更高权限,系统会直接忽略普通弹窗请求,所以CheckSelfPermission始终返回false。
额外问题:路径归属的权限限制
你要访问的路径/storage/emulated/0/Android/data/com.myApp.configuration/是其他应用的私有目录(包名是com.myApp.configuration,而非你的应用包名):
- Android 10(API 29)及以上版本启用了分区存储,即使有外部存储权限,也无法直接访问其他应用的私有目录,除非你的应用拥有
MANAGE_EXTERNAL_STORAGE权限或者是系统应用。这就是File.Exists能检测到文件,但读取时被拒绝的根本原因。
解决步骤
正确申请
MANAGE_EXTERNAL_STORAGE权限:
不要通过RequestPermissions申请,而是引导用户跳转到系统设置页面手动开启:if (!Android.OS.Environment.IsExternalStorageManager) { Intent intent = new Intent(Android.Provider.Settings.ActionManageAppAllFilesAccessPermission); intent.AddFlags(ActivityFlags.NewTask); intent.SetData(Android.Net.Uri.Parse($"package:{PackageName}")); StartActivity(intent); }注意:Google Play对该权限审核严格,仅文件管理器、备份工具等特定类型应用允许上架。
优先调整存储路径(推荐):
如果该文件是你的应用生成的,建议存储到当前应用的私有外部存储目录,无需任何特殊权限:// 获取当前应用的私有外部存储目录 string appDir = Android.App.Application.Context.GetExternalFilesDir(null).AbsolutePath; string filePath = Path.Combine(appDir, "myAppStuff.txt"); // 直接读写该路径,无需申请外部存储权限这个路径属于应用私有空间,不受分区存储限制,是Android官方推荐的存储方式。
内容的提问来源于stack exchange,提问作者DumbestGuyOnSaturn

