任务异步调用未返回会造成死锁

时间:2019-09-14 10:40:31

标签: c# xamarin

我对C#还是很陌生,所以这很容易。我正忙于创建xamarin表单应用程序,并且需要UI上下一页的数据。它的标签页,所有3个页面都需要基类返回的信息。 我遇到的问题是我需要致电google api服务获取公司信息。因此,我创建了一个异步调用。因此现在代码由于此而返回。由于选项卡式页面,我需要将数据绑定到屏幕上,现在我需要等待数据,因此基本上需要同步。

我已经尽力在该主题上找到了一切。也许我这样做的方式是错误的,但希望我的代码能证明这一点。

这是标签页:

    public BusinessTabbedPage(string PlaceId)
    {
        Children.Add(new BusinessSpecialsPage(PlaceId));
        InitializeComponent();
    }

这将是应用程序中调用viewmodel的页面之一

    public BusinessSpecialsPage(string PlaceId)
    {
        BindingContext = new BusinessSpecialsPageViewModel(PlaceId);
        InitializeComponent();
    }

由于3个页面需要相同的数据,因此我创建了一个基类。这将获取数据并将所有内容传递回UI。

    public BusinessBaseViewModel(string placeId)
    {
        Task<List<CompanyProfileModel>> task = GBDFromGoogle(placeId);
        task.Wait();
    }

    public async Task<List<CompanyProfileModel>> GBDFromGoogle(string PlaceId)
    {
        var info = await ApiGoogle.GetGoogleCompanySelectedDetails(PlaceId);
        var Companyresult = info.result;

        CompanyProfileModel CompList = new CompanyProfileModel
        {
            ContactDetails = Companyresult.formatted_phone_number,
            Name = Companyresult.name,
            Website = Companyresult.website,
        };
        ComPF.Add(CompList);

        return ComPF;
    }

这是我认为正在添加新任务然后进程死锁的api调用?

    public static async Task<GoogleSelectedPlaceDetails> GGCSD(string place_id)
    {
        GoogleSelectedPlaceDetails results = null;

        var client = new HttpClient();
        var passcall = "https://maps.googleapis.com/maps/api/place/details/json?placeid=" + place_id + "&key=" + Constants.GoogleApiKey;
        var json = await client.GetStringAsync(passcall);
        //results = await Task.Run(() => JsonConvert.DeserializeObject<GoogleSelectedPlaceDetails>(json)).ConfigureAwait(false);
        results = JsonConvert.DeserializeObject<GoogleSelectedPlaceDetails>(json);

        return results;
    }

我需要避免死锁的过程。它需要等待任务完成,这样我才能将数据返回到屏幕。

1 个答案:

答案 0 :(得分:1)

Task.Wait将阻塞当前线程,因此您永远不要在Xamarin应用程序中使用它,甚至不要在构造函数中使用它,否则,您的应用程序将冻结,直到您收到一些数据,如果服务关闭或启动时可能永远不会发生用户失去连接。

您可以在Appearing视图的ContentPage事件上初始化数据,而不用在ViewModel的构造函数中调用数据。

为此,您可以创建一个自定义behaviour甚至更好的自定义标签,可以使用以下已为您完成此操作的库:

使用NuGet:Install-Package Behaviors.Forms -Version 1.4.0

一旦安装了库,就可以使用EventHandlerBehavior将事件关联到ViewModel命令,例如:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:behaviors="clr-namespace:Behaviors;assembly=Behaviors"
             xmlns:viewModels="clr-namespace:YourApp.ViewModels"
             Title="Business Page"
             x:Class="YourApp.Views.BusinessPage">

    <ContentPage.BindingContext>
        <viewModels:BusinessViewModel />
    </ContentPage.BindingContext>

    <ContentPage.Behaviors>
        <behaviors:EventHandlerBehavior EventName="Appearing">
            <behaviors:InvokeCommandAction Command="{Binding AppearingCommand}" />
        </behaviors:EventHandlerBehavior>
    </ContentPage.Behaviors>

    [...]

ViewModel:

public BusinessBaseViewModel(string placeId)
{
    AppearingCommand = new Command(Appearing);
    PlaceId = placeId;
}

public ICommand AppearingCommand { get; private set; }

public string PlaceId { get; private set; }

private ObservableCollection<CompanyProfileModel> _googleGbd;
public ObservableCollection GoogleGbd
{
    get { _googleGbd?? (_googleGbd = new ObservableCollection<CompanyProfileModel>()); };
    set 
    {
         if (_googleGdb != value)
         {
             _googleGdb = value;
             NotifyPropertyChanged();
         }
    }
}

private async void Appearing()
{
    var companyResult = await ApiGoogle.GetGoogleCompanySelectedDetails(PlaceId);

    CompanyProfileModel companyProfile = new CompanyProfileModel
    {
        ContactDetails = companyResult.formatted_phone_number,
        Name = companyResult.name,
        Website = companyResult.website,
    };
    GoogleGbd.Add(companyProfile);
}

如果只希望在视图第一次出现时加载数据,则可以添加一个bool标志来知道您已经加载了数据。