如何在 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.Run
; Task.Run
在async
代码中首选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
方法命名约定的建议使用Async
:return 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 视图模型