Как использовать async в модели представления mvvmcross?
У меня есть длительный процесс в mvvmcross viewmodel и вы хотите сделать его асинхронным (http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx).
В настоящее время поддерживается ключевое слово async в бета-канале для Xamarin.
Ниже приведен пример того, как я в настоящее время реализую async. Флаг IsBusy может быть привязан к элементу пользовательского интерфейса и отображать загружаемое сообщение.
Правильно ли это?
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
в порядке; что один общий подход в асинхронных пользовательских интерфейсах.
Я изменил Task.Factory.StartNew
на Task.Run
; Task.Run
является предпочтительным в коде async
для причин, описанных Стивеном Туубом.
Ответ 2
Теперь MvvmCross имеет MvxAsyncCommand
(см. GitHub commit).
Итак, вместо этого
public ICommand MyCommand
{
get
{
return new MvxCommand(async () => await DoMyCommand());
}
}
Вы можете сделать это
public ICommand MyCommand
{
get
{
return new MvxAsyncCommand(DoMyCommand);
}
}
Ответ 3
Выглядит хорошо, но я бы добавил, что наконец-то поймать пробку, вот что ждет.
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, чтобы избежать кодовых таблиц (команд), и привязать ваш интерфейс непосредственно к методу асинхронного вызова.
Кроме того, если вы используете 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;
}
}
Вы можете сделать привязку как: "Нажмите" DoSomething ".
С другой стороны, вместо использования await Task.Factory.StartNew()
, почему бы не сделать _myService.LongRunningProcess
async?
Это выглядело бы намного лучше:
public async Task DoSomething()
{
IsBusy = true;
await _myService.LongRunningProcess();
IsBusy = false;
}