如何在 mvvmcross 视图模型中使用异步?

Posted

技术标签:

【中文标题】如何在 mvvmcross 视图模型中使用异步?【英文标题】:How can I use async in an mvvmcross view model? 【发布时间】:2013-06-15 17:51:24 【问题描述】:

我在 mvvmcross 视图模型中有一个长时间运行的进程,并希望使其异步 (http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx)。

Xamarin 的 beta 通道目前支持 async 关键字。

以下是我当前如何实现异步的示例。 IsBusy 标志可以绑定到 UI 元素并显示加载消息。

这是正确的方法吗?

public class MyModel: MvxViewModel

    private readonly IMyService _myService;
    private bool _isBusy;

    public bool IsBusy
    
        get  return _isBusy; 
        set  _isBusy = value; RaisePropertyChanged(() => IsBusy); ; 
    

    public ICommand MyCommand
    
        get
        
            return new MvxCommand(DoMyCommand);
        
    

    public MyModel(IMyService myService)
    
        _myService = myService;
    

    public async void DoMyCommand()
    
        IsBusy = true;
        await Task.Factory.StartNew(() =>
            
                _myService.LongRunningProcess();
            );
        IsBusy = false;
    


【问题讨论】:

【参考方案1】:

你应该避免async void。在处理ICommand 时,确实需要使用async void,但应尽量减少其范围。

此修改后的代码将您的操作公开为 async Task,它是可单元测试的并且可以从代码的其他部分使用:

public class MyModel: MvxViewModel

  private readonly IMyService _myService;
  private bool _isBusy;

  public bool IsBusy
  
    get  return _isBusy; 
    set  _isBusy = value; RaisePropertyChanged(() => IsBusy); ; 
  

  public ICommand MyCommand
  
    get
    
      return new MvxCommand(async () => await DoMyCommand());
    
  

  public MyModel(IMyService myService)
  
    _myService = myService;
  

  public async Task DoMyCommand()
  
    IsBusy = true;
    await Task.Run(() =>
    
      _myService.LongRunningProcess();
    );
    IsBusy = false;
  

您可以使用IsBusy;这是异步 UI 中的一种常见方法。

我确实将Task.Factory.StartNew 更改为Task.RunTask.Runasync 代码中首选reasons described by Stephen Toub。

【讨论】:

自从我发布了这个我已经删除了 IsBusy 标志并添加了对 IAlertsService 的调用。这有助于集中加载/警报逻辑并将其从各个视图中删除。 这很好用:return new MvxCommand(() => DoMyCommand()); async lambda 为您提供略有不同的错误处理。使用() => DoMyCommand(),来自DoMyCommand 的任何异常都会被默默吞下。使用async () => await DoMyCommand(),来自DoMyCommand 的任何异常都被视为未处理的异常。 @ChrisKoiak,您能否在博客文章或文章中举例说明您如何实现和使用 IAlertService,或者只是在这里进行概念性解释? @Vackup:我倾向于遵循这个答案中的简单模式,直到我需要更复杂的东西。自定义“异步命令”可以更好地处理一些用例:繁忙的微调器/禁用时执行、取消命令、数据绑定结果/错误。 My latest async command stuff is here.【参考方案2】:

MvvmCross 现在有 MvxAsyncCommand(参见 GitHub commit)。

所以不要这样做

public ICommand MyCommand

  get
  
    return new MvxCommand(async () => await DoMyCommand());
  

你可以这样做

public ICommand MyCommand

  get
  
    return new MvxAsyncCommand(DoMyCommand);
  

【讨论】:

按照async 方法命名约定的建议使用Asyncreturn new MvxAsyncCommand(DoMyCommandAsync);【参考方案3】:

看起来不错,只是我最终会在那个 await 周围添加一个 try catch。

    public async void DoMyCommand()
    
        IsBusy = true;
        try
            await Task.Factory.StartNew(() =>
                                        
                _myService.LongRunningProcess();
            );
        catch
            //Log Exception
        finally
            IsBusy = false;
        
    

此外,我的博客上有一个使用异步 MvxCommand 的示例。非常类似于您的示例http://deapsquatter.blogspot.com/2013/03/updating-my-mobile-apps-for-async.html

【讨论】:

【参考方案4】:

您还可以使用 MethodBinding plugin 来避免样板代码(命令),并将您的 UI 直接绑定到异步方法。

此外,如果您使用Fody PropertyChanged,您的代码将如下所示:

[ImplementPropertyChanged]
public class MyModel: MvxViewModel

    private readonly IMyService _myService;

    public bool IsBusy  get; set; 

    public MyModel(IMyService myService)
    
        _myService = myService;
    

    public async Task DoSomething()
    
        IsBusy = true;
        await Task.Factory.StartNew(() =>
        
                _myService.LongRunningProcess();
        );
        IsBusy = false;
    

您可以像这样进行绑定:“Click DoSomething”。

另一方面,与其使用await Task.Factory.StartNew(),为什么不让_myService.LongRunningProcess 异步呢? 看起来会好很多:

public async Task DoSomething()

    IsBusy = true;
    await _myService.LongRunningProcess();
    IsBusy = false;

【讨论】:

以上是关于如何在 mvvmcross 视图模型中使用异步?的主要内容,如果未能解决你的问题,请参考以下文章

使用 mvvmcross,我如何“共享”一个视图模型以使用 iphone 的视图和 ipad 的另一个视图?有啥特殊的命名约定吗?

在 mvvmcross 中显示非视图/视图模型的视图

Mvvmcross - 从常规活动中显示 mvvmcross 视图模型

MvvMCross 导航回多个视图模型/截断导航堆栈

如何通过 Xamarin MvvmCross 中的 BottomNavigationView 在视图模型之间导航

使用 mvvmcross 显示视图模型时无法解析当前的***活动