如何正确使用async / await来显示Xamarin Forms ActivityIndi​​cator

时间:2017-09-05 23:36:49

标签: c# asynchronous xamarin.forms portable-class-library

我有选择ListView项时执行的代码。由于此代码可能需要花费大量时间才能执行,因此我希望在运行时显示ActivityIndicator

我已经阅读了很多关于ActivityIndicator的帖子,我相信我已经正确设置了它。我很确定我的问题是由于运行代码阻塞UI线程,因为UI在此过程中完全冻结。

我已经完成了我能找到的所有使用async / await但没有运气的变化,所以我假设我仍在做错误的事情。

这是我的代码:

private ListItem selectedList;
public ListItem SelectedList
{
    get
    {
        return selectedList;
    }
    set
    {
        if (selectedList != value)
        {
            var oldValue = selectedList;
            selectedList = value;
            buildThisList();
        }
    }
}

private async void buildThisList()
{
    StatusBarIsVisibile = true; 
    ThisList = Task.Run(async () =>
    {
        return await buildBnBList(SelectedList, selectedLocation, 
                         thisYearList.ListId)
                         .ConfigureAwait(false);
    }).Result;  // 1.8 seconds
    StatusBarIsVisibile = false;
    ThisList.MainVM = this;
    ThisList.BuildView();
    PIShowListHeaderText = SelectedList.Title.Trim();
}

private async Task<BnBList> buildBnBList(ListItem pSelectedList, State pselectedLocation, int pListId)
{
    BnBList newList = new BnBList(pSelectedList, pselectedLocation, pListId);
    List<BirdsnBflysMaster> bbfList = 
         App.BnBRepo.GetBirdsnBflys(selectedList.ListType); // 1.3 seconds
    Lists parentList = App.BnBRepo.GetAList(SelectedList.ListId); // .5 seconds
    BnBItem newBnBItem;
    foreach (BirdsnBflysMaster bbf in bbfList) // .4 seconds
    {
        newBnBItem = new BnBItem(parentList, bbf, selectedLocation, newList, thisYearList.ListId);
        newList.ListAll.Add(newBnBItem);
        if (newBnBItem.ListTypeID == "Bird")
        {
            ...
        }
        else
        {
            ...
        }
    }            
    return newList;
}

所以:

    {li> buildThisListSelectedList属性更改时被调用。
  • buildThisList调用buildBnBList,这是一个耗时的过程。
  • buildBnBList中没有任何异步完成的内容。
  • StatusBarIsVisible绑定到IsVisible的{​​{1}}和IsRunning

如何对此进行编码,以便执行ActivityIndicator不会阻止UI线程?

1 个答案:

答案 0 :(得分:4)

是的,您使用异步代码进行混合阻止(.Result),这可能导致死锁。应避免async void,除非在偶数处理程序中。

那么为什么不这样做呢?

创建一个事件和事件处理程序。

public event EventHandler ListSelected = delegate { }

现在应该允许您创建一个事件处理程序

private async void OnListSelected(object sender, EventArgs e) {
    StatusBarIsVisibile = true; 
    ThisList = await buildBnBList(SelectedList, selectedLocation, thisYearList.ListId);

    StatusBarIsVisibile = false;
    ThisList.MainVM = this;
    ThisList.BuildView();
    PIShowListHeaderText = SelectedList.Title.Trim();
}

您显然会订阅活动(当然在生命周期的早期)

this.ListSelected += OnListSelected;

并根据需要举起活动

private ListItem selectedList;
public ListItem SelectedList {
    get {
        return selectedList;
    }
    set {
        if (selectedList != value) {
            var oldValue = selectedList;
            selectedList = value;
            OnListSelected(this, EventArgs.Empty);
        }
    }
}