Xamarin Forms适配Android 13(API33)后外部存储读写权限问题求助
Android 13(API33)下Xamarin.Forms外部存储权限问题解决方案
问题核心
升级目标版本到Android 13后,WRITE_EXTERNAL_STORAGE和READ_EXTERNAL_STORAGE权限被系统直接拒绝——因为Android 13彻底废弃了这两个权限用于非媒体文件的访问。应用需要存储可被用户通过文件浏览器/USB访问的文档、日志,既不能放在媒体目录,也无法使用应用私有存储(用户无法直接访问)。
合规解决方案:使用存储访问框架(SAF)
这是Google推荐的方式,无需特殊权限,让用户自主选择存储位置,完全符合权限政策。
1. 实现目录选择与文件写入(Android项目层)
先在Forms层定义接口,再在Android项目中实现具体逻辑:
// Forms层定义接口 public interface IStorageService { Task<string> PickFolderAsync(); Task CreateLogFileAsync(string folderUri, string fileName, string content); } // Android项目中的实现类 [assembly: Dependency(typeof(StorageService))] namespace YourApp.Droid { public class StorageService : IStorageService { public async Task<string> PickFolderAsync() { var intent = new Intent(Intent.ActionOpenDocumentTree); intent.AddFlags(ActivityFlags.GrantReadUriPermission); intent.AddFlags(ActivityFlags.GrantWriteUriPermission); intent.AddFlags(ActivityFlags.GrantPersistableUriPermission); // 可选:默认打开系统Documents目录 var initialUri = Android.Net.Uri.Parse("content://com.android.externalstorage.documents/document/primary%3ADocuments"); intent.PutExtra(DocumentsContract.ExtraInitialUri, initialUri); var activity = Platform.CurrentActivity; var result = await activity.StartActivityForResultAsync(intent, 100); if (result.ResultCode == Result.Ok && result.Data != null) { var selectedUri = result.Data.Data; // 持久化权限,重启应用后仍可访问该目录 activity.ContentResolver.TakePersistableUriPermission(selectedUri, ActivityFlags.GrantReadUriPermission | ActivityFlags.GrantWriteUriPermission); return selectedUri.ToString(); } return null; } public async Task CreateLogFileAsync(string folderUri, string fileName, string content) { var uri = Android.Net.Uri.Parse(folderUri); var documentUri = DocumentsContract.CreateDocument(Platform.CurrentActivity.ContentResolver, uri, "text/plain", fileName); using (var stream = Platform.CurrentActivity.ContentResolver.OpenOutputStream(documentUri)) using (var writer = new StreamWriter(stream)) { await writer.WriteAsync(content); } } } }
2. 在Forms层调用
// 示例:选择存储目录并创建日志文件 var folderUri = await DependencyService.Get<IStorageService>().PickFolderAsync(); if (!string.IsNullOrEmpty(folderUri)) { await DependencyService.Get<IStorageService>().CreateLogFileAsync(folderUri, "app_log.txt", $"[{DateTime.Now}] 应用启动日志"); }
特殊场景方案:申请MANAGE_EXTERNAL_STORAGE权限
如果你的应用是文件管理器类工具,确实需要全外部存储访问权限,可以使用此方案,但需注意Google Play审核要求,必须明确说明权限的必要性,否则会被拒审。
1. 声明权限(AndroidManifest.xml)
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <!-- 兼容Android 12及以下版本 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
2. 权限请求实现
public async Task<bool> CheckAndRequestManageStorage() { if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.R) { if (!Environment.IsExternalStorageManager) { var intent = new Intent(Settings.ActionManageAppAllFilesAccessPermission); intent.SetData(Android.Net.Uri.Parse($"package:{Platform.CurrentActivity.PackageName}")); Platform.CurrentActivity.StartActivity(intent); // 等待用户操作后检查权限状态 await Task.Delay(2000); return Environment.IsExternalStorageManager; } return true; } // 兼容Android 11及以下版本 var writeStatus = await Permissions.CheckStatusAsync<Permissions.StorageWrite>(); if (writeStatus != PermissionStatus.Granted) { var result = await Permissions.RequestAsync<Permissions.StorageWrite>(); return result == PermissionStatus.Granted; } return true; }
失效原因说明
Android 13对非媒体文件的外部存储权限做了彻底调整:
WRITE_EXTERNAL_STORAGE和READ_EXTERNAL_STORAGE权限被废弃,系统会直接拒绝此类权限请求- 应用专属外部存储目录(
Android/data/包名/)无需权限,但属于私有目录,用户无法直接访问(需手动开启文件管理器的隐藏文件显示)
内容的提问来源于stack exchange,提问作者Michael Bedford
相关产品推荐
相关产品推荐

