Xamarin.Forms DependencyService 无法在文件处理中跨平台共享接口

Xamarin.Forms DependencyService cannot share interface cross-platform in file handling

我正在关注 official Xamarin guide 使用 .net-standard-2.0 PCL 项目进行跨平台文件处理。该应用程序旨在为 Android 和 Windows.

构建

这让我想到了一个问题。接口代码如下

public interface ISaveAndLoad {
    void SaveText (string filename, string text);
    string LoadText (string filename);
}

然而,UWP给出的代码写成

[assembly: Dependency(typeof(SaveAndLoad_WinApp))]
namespace WindowsApp
{
    // https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh758325.aspx
    public class SaveAndLoad_WinApp : ISaveAndLoad
    {
        public async Task SaveTextAsync(string filename, string text)
        {
            StorageFolder localFolder = ApplicationData.Current.LocalFolder;
            StorageFile sampleFile = await localFolder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);
            await FileIO.WriteTextAsync(sampleFile, text);
        }
        public async Task<string> LoadTextAsync(string filename)
        {
            StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
            StorageFile sampleFile = await storageFolder.GetFileAsync(filename);
            string text = await Windows.Storage.FileIO.ReadTextAsync(sampleFile);
            return text;
        }
    }
}

代码将无法编译,因为 SaveAndLoad_WinApp 未实现接口 ISaveAndLoad

我知道我可以将界面更改为TaskTask<string>,所以它会编译。

然而问题又回到了Android。 UWP 正在使用这个 Windows.Storage.FileIO 在 Android 上不存在的东西。当 Android 使用 System.IO 时,它没有可以等待的异步方法。这意味着我无法共享界面,因为 UWP 需要 async Task<string> 但 Android 仅 return 是 string 而不是 Task

如果我不在 UWP 方法中使用 async,则 FileIO 将不起作用,反之亦然。 async void for SaveText 工作正常,因为界面状态为 void。但是,它不允许我为 LoadText 创建 async string,因为异步方法必须 return a Task<T>Taskvoid

似乎要走的路也会在 Android 上实现异步,同时让代码 运行 同步。但是,我觉得我做错了什么,因为 Xamarin 示例显示它应该可以工作。

我是不是误解了什么或者遗漏了一个实现?或者它在 Xamarin 文档中实际上是错误的?


更新#1 我已经为 SaveText 设置了 async void 并意识到存在未等待线程的问题。它快速转发到下一个功能。所以我实际上必须等待 UWP PCL 中的函数。然而,Android 上的实施是无法等待的 void。所以它实际上导致了错误...... 对如何实现这个场景有什么建议吗?

这是我在 PCL

中输入的代码
public static void Save(){
    DependencyService.Get<ISaveAndLoad>().SaveText("temp.txt", input.Text);
}

The code will not compile as SaveAndLoad_WinApp does not implement the interface ISaveAndLoad.

您可以下载文档示例WorkingWithFiles, you could find it here. In this sample the ISaveAndLoad界面有点不同:

public interface ISaveAndLoad
{
    Task SaveTextAsync (string filename, string text);
    Task<string> LoadTextAsync (string filename);
    bool FileExists (string filename);
}

UWP is using this Windows.Storage.FileIO thing which does not exists on Android. While Android is using System.IO, it does not have a Async method that can be awaited.

可以参考SaveAndLoad_Android.cs class in the sample project, use StreamWriter.WriteAsync()方法:

public class SaveAndLoad_Android : ISaveAndLoad
{
    #region ISaveAndLoad implementation

    public async Task SaveTextAsync (string filename, string text)
    {
        var path = CreatePathToFile (filename);
        using (StreamWriter sw = File.CreateText (path))
            await sw.WriteAsync(text);
    }

    public async Task<string> LoadTextAsync (string filename)
    {
        var path = CreatePathToFile (filename);
        using (StreamReader sr = File.OpenText(path))
            return await sr.ReadToEndAsync();
    }

    public bool FileExists(string filename)
    {
        return File.Exists(CreatePathToFile(filename));
    }

    #endregion

    string CreatePathToFile(string filename)
    {
        var docsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
        return Path.Combine(docsPath, filename);
    }
}

然后在您的 PCL 中使用 SaveTextAsync 方法,如下所示:

var fileService = DependencyService.Get<ISaveAndLoad> ();
...
saveButton.Clicked += async (sender, e) => 
{
    loadButton.IsEnabled = saveButton.IsEnabled = false;
    // uses the Interface defined in this project, and the implementations that must
    // be written in the iOS, Android and WinPhone app projects to do the actual file manipulation

    await fileService.SaveTextAsync (fileName, input.Text);
    loadButton.IsEnabled = saveButton.IsEnabled = true;
};