从ViewModel构造函数Xamarin.Forms调用异步方法

时间:2017-05-18 20:37:55

标签: asynchronous xamarin constructor xamarin.forms async-await

我无法从a-z中找到一个关于如何以安全的方式从构造函数中实现调用异步方法的直接示例。以下是我的想法,但我不明白这些概念,所以我不知道它是否真的正确。有人可以祝福这种格式吗?

创建IAsyncInitialization接口:

/// <summary>
/// The result of the asynchronous initialization of this instance.
/// see http://blog.stephencleary.com/2013/01/async-oop-2-constructors.html
/// </summary>
Task Initialization { get; }

在此ViewModel上暂停界面,然后......:

public GotoViewModel() // constructor
{
    Initialization = InitializeAsync();
}

public Task Initialization { get; private set; }
private async Task InitializeAsync()
{
    //call some async service and get data
}

来自使用此ViewModel的代码隐藏xaml.cs:

public partial class GotoPage : ContentPage, IAsyncInitialization
{
    IGotoViewModel VM;
    public GotoPage()
    {
         InitializeComponent();
         VM = App.Container.Resolve<IGotoViewModel>();
         Initialization = InitializeAsync();
     }

     public Task Initialization { get; private set; }

     private async Task InitializeAsync()
     {
          await VM.Initialization;
          this.BindingContext = VM;
     }
}

这段代码效果很好,但我知道这并不重要。

3 个答案:

答案 0 :(得分:7)

可能即将推出的关于c#的未来版本是拥有异步构造函数的能力,但在我们达到这个令人敬畏的未来之前,这是您的选择。

您可以使用

.Wait()

但是,在执行此操作时,您需要非常了解正在运行的线程。如果在UI线程中调用VM,则将锁定UI,直到异步任务完成。如果异步任务也在UI线程中使用了某些东西,那么这里就会出现死锁。

在某些条件下缓解这种情况的方法是通过

Task.Run(async () => { await Initialize(); }).Wait();

但同样,它并非万无一失,可能存在死锁等待。

另一个选择是做一些非常高级的东西,即使我不完全理解这段代码在做什么,但它确实有效。

如果你看Exrin ThreadHelper.cs它包含方法

public static void RunOnUIThread(Func<Task> action)

这允许您在同一个UIThread中运行任务,同时不阻止它。因为有时您需要在UI线程中运行异步任务,然后等待它。这很复杂,但可能。

现在您已经知道为什么从构造函数执行异步操作非常棘手。这是简单的方法。

由于您的页面绑定到ViewModel,因此最好的方法是将OnAppearing方法转发给它。因此,在您的页面中,您可以

public async void OnAppearing()
{
    await MyViewModelInstance.OnAppearing();
}

然后在你的ViewModel中你可以做到

public async Task OnAppearing()
{
    await InitializeAsync();
}
如果您确定调用该方法的人不需要等待事件完成的任务,那么

async void是完全可以接受的。

这种方式初始化在View出现时运行而不在其构造上运行。该方法适用于App.xaml.cs,您可以在OnStart中执行此操作,而不是构造函数。

答案 1 :(得分:0)

您可以使用像FreshMvvm这样的简单MVVM-Framework,而不是在构造函数中调用它或使用Adam Pedley提到的OnAppearing方法。在FreshMVVM中,您可以覆盖init()方法来初始化对象。

在我当前的项目中,我使用Autofac初始化我的ViewModel 作为IoC-Container,在Autofac创建其实例后,在ViewModel中加载内容。

答案 2 :(得分:0)

自从我发布了这个问题以来,在我的项目中已经有一年多的时间了,我一直没有任何MVVM框架,因为我受不了可能与最新Xamarin.Forms兼容的框架的束缚。我最终从与行为相关的xaml出现事件中,在我的viewmodel中调用了我的async init方法。通过查看我创建的here on github

的Xamarin Starter项目,您可以看到一个示例。