Xamarin.Forms Android 10 SD卡访问权限激活方法咨询
你碰到的问题核心是Android 10引入了Scoped Storage(分区存储)机制,它彻底改变了应用访问外部存储的规则——哪怕你声明了存储权限,也没法再像旧版本那样直接通过文件路径读写公共目录(比如/storage/emulated/0/Documents)。下面分步骤给你具体的解决方法:
1. 先声明必要的权限
首先在你的AndroidManifest.xml里添加存储权限声明:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 若应用目标API是30+(Android 11及以上)需额外添加,Android 10可选 --> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:minSdkVersion="30" />
2. 动态申请运行时权限
存储权限属于「危险权限」,Android 6.0及以上必须在运行时请求用户授权。你可以在Activity里添加以下代码:
private const int RequestStoragePermissions = 1; private void RequestPermissions() { string[] permissions = { Android.Manifest.Permission.ReadExternalStorage, Android.Manifest.Permission.WriteExternalStorage }; if (ActivityCompat.CheckSelfPermission(this, Android.Manifest.Permission.ReadExternalStorage) != Permission.Granted || ActivityCompat.CheckSelfPermission(this, Android.Manifest.Permission.WriteExternalStorage) != Permission.Granted) { ActivityCompat.RequestPermissions(this, permissions, RequestStoragePermissions); } else { // 权限已授予,执行文件操作逻辑 AccessDocumentsDirectory(); } } // 处理权限申请结果 public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults) { base.OnRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == RequestStoragePermissions) { if (grantResults.Length > 0 && grantResults[0] == Permission.Granted) { AccessDocumentsDirectory(); } else { Toast.MakeText(this, "需要存储权限才能访问文件", ToastLength.Short).Show(); } } }
3. 适配Scoped Storage:用SAF访问Documents目录
Android 10下,就算拿到了存储权限,也不能直接用System.IO.Directory.GetFiles访问公共Documents目录——你得用**Storage Access Framework(SAF)**让用户手动选择目录,再通过ContentResolver来访问文件。
下面是获取Documents目录文件列表的示例代码:
private const int RequestOpenDocumentTree = 2; private void AccessDocumentsDirectory() { // 打开系统文件选择器,引导用户选择Documents目录 Intent intent = new Intent(Intent.ActionOpenDocumentTree); // 设置默认打开Documents目录 Uri uri = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDocuments).Uri; intent.PutExtra("android.provider.extra.INITIAL_URI", uri); StartActivityForResult(intent, RequestOpenDocumentTree); } // 处理目录选择结果 protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) { base.OnActivityResult(requestCode, resultCode, data); if (requestCode == RequestOpenDocumentTree && resultCode == Result.Ok && data != null) { Uri treeUri = data.Data; // 持久化URI权限,下次启动无需重复选择 ContentResolver.TakePersistableUriPermission(treeUri, IntentFlags.GrantReadUriPermission); // 遍历目录下的文件 DocumentFile rootDoc = DocumentFile.FromTreeUri(this, treeUri); if (rootDoc != null && rootDoc.IsDirectory) { foreach (DocumentFile file in rootDoc.ListFiles()) { string fileName = file.Name; // 若需读取文件内容,可通过ContentResolver打开输入流 // Stream stream = ContentResolver.OpenInputStream(file.Uri); } } } }
为什么原来的代码会报错?
Android 10默认启用Scoped Storage,它把应用的存储访问限制在私有目录,以及通过SAF/MediaStore访问的内容里。GetExternalStoragePublicDirectory虽然能返回路径,但应用没有直接访问该路径的权限,所以调用Directory.GetFiles会抛出权限拒绝错误。
如果你只是需要访问自己应用生成的文件,也可以考虑用应用的私有外部存储目录(Context.GetExternalFilesDir(null)),这个目录不需要额外权限就能访问,还不受Scoped Storage限制。
内容的提问来源于stack exchange,提问作者Prino

