web-dev-qa-db-ja.com

mvvmcrossビューモデルで非同期を使用するにはどうすればよいですか?

Mvvmcrossビューモデルで長時間実行されているプロセスがあり、非同期にしたい( http://msdn.Microsoft.com/en-us/library/vstudio/hh191443.aspx )。

Asyncキーワードは現在、Xamarinのベータチャネルでサポートされています。

以下は、私が現在非同期を実装している方法の例です。 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;
    }

}
26
Chris Koiak

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の一般的なアプローチの1つです。

Task.Factory.StartNewTask.Runに変更しました。 Task.Runは、 Stephen Toubによって説明された理由asyncコードで優先されます。

30
Stephen Cleary

MvvmCrossにMvxAsyncCommandが追加されました(GitHub commit を参照)。

だからこれをする代わりに

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

あなたはこれを行うことができます

public ICommand MyCommand
{
  get
  {
    return new MvxAsyncCommand(DoMyCommand);
  }
}
6
Colin Bacon

最終的にその待機の周りにトライキャッチを追加することを除いて、問題ないように見えます。

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

さらに、asyncでMvxCommandを使用した例がブログにあります。あなたの例と非常によく似ています http://deapsquatter.blogspot.com/2013/03/updating-my-mobile-apps-for-async.html

3
Kevin

MethodBindingplugin を使用して、ボイラープレートコード(コマンド)を回避し、非同期メソッドに直接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;
    }
}
_

「DoSomethingをクリック」のようにバインディングを作成できます。

一方、await Task.Factory.StartNew()を使用するのではなく、__myService.LongRunningProcess_を非同期にしてみませんか?それははるかに良く見えるでしょう:

_public async Task DoSomething()
{
    IsBusy = true;
    await _myService.LongRunningProcess();
    IsBusy = false;
}
_
1
xleon