
Avalonia Android 存储权限适配指南IStorageProvider 与 Android 13 分区存储完整上手【免费下载链接】AvaloniaDevelop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The future of .NET UI项目地址: https://gitcode.com/GitHub_Trending/ava/Avalonia如果你要用 Avalonia 的 IStorageProvider 接口让 Android 应用读取和保存文件这篇文章就是为你写的讲清 Android 13API 33分区存储系统按目录隔离文件访问权限的机制带来的变更并给出可落地的适配步骤。读完你可以让文件选择与保存功能在 Android 10 至 14 设备上不再抛SecurityException。背景速览Android 13API 33起WRITE_EXTERNAL_STORAGE权限声明不再授予任何能力READ_EXTERNAL_STORAGE对媒体类目录同样失效。直接拼路径读写公共目录如/storage/emulated/0/DCIM的做法全部被分区存储拦截旧代码运行时抛SecurityException。影响面所有在 Android 目标上直接访问文件系统的 Avalonia 应用。官方示例 ControlCatalog.Android 的 AndroidManifest.xml 中至今保留着旧式权限声明就是需要按本文修改的典型样本。原理一分钟核心结论一句话新版 Android 上申请权限要换成系统文件选择器。路径权限要求说明应用私有目录GetExternalFilesDir无需权限任何时候都可直接读写AndroidStorageProvider.cs 中TryGetWellKnownFolderAsync返回的就是这个目录用户经系统选择器选中的文件无需运行时权限选择器授权一次 URI 级访问有效期为本次会话公共目录直接拼路径API 33 禁止无论清单里声明了什么权限Avalonia 在 Android 上的实现AndroidStorageProvider已按这套规则写好OpenFilePickerAsync内部发ACTION_OPEN_DOCUMENTIntent 拉起系统选择器你只管处理返回的IStorageFile。落地步骤第 1 步清理清单里的过期权限。清单里留着WRITE_EXTERNAL_STORAGE不会报错但在 API 33 设备上它是无效声明还会误导后来维护的人。按目标版本条件保留旧权限!-- 仅旧版设备需要API 33 已失效按 minSdk 决定是否保留 -- uses-permission android:nameandroid.permission.READ_EXTERNAL_STORAGE android:maxSdkVersion32 /做完后你用aapt dump permissions查看 APK权限列表里不再出现WRITE_EXTERNAL_STORAGE。第 2 步旧设备补一次运行时请求。分区存储只管 API 30 设备Android 10 以下应用直接读外部文件仍需运行时授权这段请求逻辑放在 MainActivity.cs 的OnCreate里protected override void OnCreate(Bundle? savedInstanceState) { base.OnCreate(savedInstanceState); // 仅 API 32 及以下需要运行时请求33 走选择器不申请存储权限 if (Build.VERSION.SdkInt BuildVersionCodes.Tiramisu) { RequestPermissions(new[] { Manifest.Permission.ReadExternalStorage }); } }做完后在 Android 9 设备上启动应用系统会弹出一次性授权弹窗且仅首次出现。第 3 步新设备用IStorageProvider走选择器。这是推荐路径——不声明、不请求任何存储权限授权由系统选择器完成。完整写法可参考官方示例 DialogsPage.xaml.cs// 从当前控件所在窗口拿到平台存储提供器 var provider TopLevel.GetTopLevel(this)!.StorageProvider; var files await provider.OpenFilePickerAsync(new FilePickerOpenOptions { Title 选择图片, AllowMultiple true, FileTypeFilter new[] { FilePickerFileTypes.Images } // 限定 MIME 类型 }); using var stream await files[0].OpenReadAsync(); // 返回流逐文件读取上图即示例项目 Assets 中用于展示图片加载效果的素材通过上面的选择器拿到IStorageFile后读成流再交给Bitmap即可显示。第 4 步保存走SaveFilePickerAsync。想写文件不要猜路径用SaveFilePickerAsync生成带写入授权的 URI跨进程重启后要再次访问同一文件则用CreateBookmarkAsync生成书签书签是一段可序列化的字符串Android 端底层是 URI 编码见AndroidStorageProvider中的DecodeUriFromBookmark。做完后在 API 33 设备上完整跑一遍选图 → 保存流程无任何权限弹窗、无异常。排错手册常见报错原因修复SecurityException: Permission Denial: opening provider ...ExternalStorageProvider用绝对路径直读公共目录改走OpenFilePickerAsync或写入GetExternalFilesDir私有目录选择后文件打开抛FileNotFoundException选择器返回的是content://虚拟路径不能按磁盘路径打开只通过IStorageFile.OpenReadAsync()读流需要落盘时CopyToAsync到自己目录升级 13 后原本可用的读取突然失败仍依赖READ_EXTERNAL_STORAGE授权该权限 API 33 起无效改用选择器或MediaStore查询多文件选择只拿到 1 个选择器返回ClipData旧代码只读了Data字段升级到当前版Avalonia.AndroidStartActivity已处理ClipData验收清单Android 13/14 真机上不声明任何存储权限选图→读取→保存全链路无异常Android 9 真机上首次点击读取外部文件时弹出权限弹窗拒绝后功能给出友好降级AndroidManifest.xml中已移除WRITE_EXTERNAL_STORAGE或加maxSdkVersion限定应用私有目录GetExternalFilesDir写入成功且全程无权限弹窗重启应用后用CreateBookmarkAsync生成的书签仍能打开原文件Avalonia 通过IStorageProvider抽象屏蔽了 Android 分区存储的权限细节选文件走系统选择器、写文件走SaveFilePickerAsync清单里只留条件限定的旧权限即可。下一步建议对照 AndroidStorageProvider.cs 通读一遍实现确认你的调用方式与框架假设一致。【免费下载链接】AvaloniaDevelop Desktop, Embedded, Mobile and WebAssembly apps with C# and XAML. The future of .NET UI项目地址: https://gitcode.com/GitHub_Trending/ava/Avalonia创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考