You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin Forms中Android端重复写入可访问外部存储的问题

解决方案:Xamarin Forms跨平台实现可访问目录文件写入

要在UWP和Android(11/12)平台实现文件自动保存到文件管理器可访问的位置(如下载文件夹),同时保持跨平台兼容性,需要使用**Xamarin依赖服务(Dependency Service)**实现平台特定的存储逻辑,核心思路是在共享代码中定义接口,各平台分别实现对应逻辑。

步骤1:定义跨平台存储接口

在共享项目中创建一个接口,统一文件操作的方法:

public interface IFileStorageService
{
    // 获取可被文件管理器访问的文件路径(UWP返回物理路径,Android返回文件名标识)
    Task<string> GetPublicDataFilePathAsync();
    // 向文件追加内容(支持持续写入)
    Task AppendTextToFileAsync(string content);
}

步骤2:Android平台实现

Android 11/12采用Scoped Storage机制,不能直接访问公共目录的物理路径,需通过MediaStore写入到Downloads文件夹。

配置AndroidManifest权限

更新AndroidManifest.xml,添加必要权限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="30" />

实现存储服务类

在Android项目中创建实现类,通过ContentResolver操作MediaStore:

[assembly: Dependency(typeof(AndroidFileStorageService))]
namespace YourAppNamespace.Droid
{
    public class AndroidFileStorageService : IFileStorageService
    {
        private readonly string _fileName = "data.txt";
        private readonly string _mimeType = "text/plain";

        public Task<string> GetPublicDataFilePathAsync()
        {
            // Scoped Storage下无法直接返回物理路径,返回文件名用于标识
            return Task.FromResult(_fileName);
        }

        public async Task AppendTextToFileAsync(string content)
        {
            var contentValues = new ContentValues();
            contentValues.Put(MediaStore.MediaColumns.DisplayName, _fileName);
            contentValues.Put(MediaStore.MediaColumns.MimeType, _mimeType);
            contentValues.Put(MediaStore.MediaColumns.RelativePath, Environment.DirectoryDownloads);

            var resolver = Android.App.Application.Context.ContentResolver;
            var fileUri = resolver.Insert(MediaStore.Downloads.ExternalContentUri, contentValues);

            if (fileUri == null)
                throw new IOException("无法在Downloads目录创建文件");

            using (var outputStream = resolver.OpenOutputStream(fileUri))
            using (var writer = new StreamWriter(outputStream))
            {
                await writer.WriteLineAsync(content);
                await writer.FlushAsync();
            }
        }
    }
}

添加动态权限申请

Android 6.0+需要动态申请存储权限,在MainActivity中添加权限处理:

protected override void OnCreate(Bundle savedInstanceState)
{
    base.OnCreate(savedInstanceState);
    RequestStoragePermissions();
}

private void RequestStoragePermissions()
{
    if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
    {
        var permissions = new[] { Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage };
        if (CheckSelfPermission(Manifest.Permission.WriteExternalStorage) != Permission.Granted)
            RequestPermissions(permissions, 100);
    }
}

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
    Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}

步骤3:UWP平台实现

UWP直接通过KnownFolders访问Downloads文件夹,实现逻辑更简单:

[assembly: Dependency(typeof(UwpFileStorageService))]
namespace YourAppNamespace.UWP
{
    public class UwpFileStorageService : IFileStorageService
    {
        private StorageFile _dataFile;

        public async Task<string> GetPublicDataFilePathAsync()
        {
            var downloadsFolder = KnownFolders.DownloadsFolder;
            _dataFile = await downloadsFolder.CreateFileAsync("data.txt", CreationCollisionOption.OpenIfExists);
            return _dataFile.Path;
        }

        public async Task AppendTextToFileAsync(string content)
        {
            if (_dataFile == null)
                await GetPublicDataFilePathAsync();
            
            await FileIO.AppendTextAsync(_dataFile, content + Environment.NewLine);
        }
    }
}

步骤4:共享代码中调用服务

在共享项目的业务逻辑中,通过DependencyService获取平台实现,调用写入方法:

// 获取存储服务实例
var storageService = DependencyService.Get<IFileStorageService>();

// 持续写入数据示例
await storageService.AppendTextToFileAsync("蓝牙设备记录数据:" + DateTime.Now.ToString());

关键说明

  1. 跨平台兼容:通过依赖服务隔离平台特定代码,共享代码无需直接引用Android/UWP专属API,保留UWP兼容性。
  2. Android Scoped Storage:替代已废弃的GetExternalStoragePublicDirectory,使用MediaStore写入公共Downloads目录,符合Android 11/12的存储规范。
  3. 持续写入:AppendTextToFileAsync方法支持每次调用追加内容,满足记录数据的需求。

内容的提问来源于stack exchange,提问作者Trevor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 10:05:30