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

Posted

技术标签:

【中文标题】从 ViewModel 构造函数 Xamarin.Forms 调用异步方法【英文标题】:Calling Asynchronous method from ViewModel Constructor Xamarin.Forms 【发布时间】:2017-10-18 20:01:12 【问题描述】:

我在 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;
     

这段代码效果很好,但我知道这并不意味着什么。

【问题讨论】:

【参考方案1】:

自从我发布这个问题以来,在我的项目中已经有一年多的时间了,我一直没有使用任何 MVVM 框架,因为我无法忍受被绑定到可能与最新 Xamarin.Forms 冲突的框架。我最终从与行为相关的 xaml 出现事件中调用了我的视图模型中的异步初始化方法。

【讨论】:

【参考方案2】:

除了在构造函数中调用它或使用 Adam Pedley 提到的 OnAppearing 方法,您还可以使用简单的 MVVM 框架,例如 FreshMvvm。在 FreshMVVM 中,您可以重写 init() 方法来初始化您的对象。

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

【讨论】:

【参考方案3】:

可能即将推出的 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 是完全可以接受的,如果您确定调用该方法的人不需要任务来等待事件完成。

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

【讨论】:

我知道我们不应该说谢谢,但我已经阅读了您的一些博客。感谢您在社区中如此活跃! 据我所知,在使用async void方法时,你需要确定它不会抛出异常,因为那样该异常无处可冒,它会触发全局异常处理程序/使应用程序崩溃。我总是总是将我的异步 void 方法包装在 try catch 中,并优雅地处理任何意外异常(如果处于调试模式,则记录、中断调试器等)

以上是关于从 ViewModel 构造函数 Xamarin.Forms 调用异步方法的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Xamarin.Forms.DependencyService 注入具有构造函数注入的 ViewModel

如何将相同的viewmodel设置为xamarin表单中的新mvvm中的两个视图

数据应该在哪里加载到 ViewModel

Xamarin 表单从 listview 绑定到 viewmodel

从 ViewModel 绑定到 Xamarin.Forms.Maps.Map

从 ViewModel Xamarin 中的动态表单中获取条目值