【问题标题】:MvvmCross UI freeze when calling async method during initialization在初始化期间调用异步方法时 MvvmCross UI 冻结
【发布时间】:2014-03-01 19:31:00
【问题描述】:

当我在 ViewModel 初始化期间尝试加载 API 端点的内容时,我遇到了 Xamarin.Android/iOS 上的 MvvmCross 问题。我正在从 API 加载某个区域的当前天气预报,并将其写入异步方法,并从初始化生命周期回调中调用它。到目前为止,我已经尝试过 Stat() 和 Init()。在这两种情况下,我的 UI 都加载了 ViewModel 中的值,但随后立即冻结。如果我在异步代码的任何位置设置断点,断点就会捕获并在播放时,它会按预期工作。我怀疑某处存在某种竞争条件,但我不确定如何避免它并在我的 ViewModel/View 出现时加载数据。这是我的代码:

//天气服务

public async Task<WeatherStatus> GetWeatherStatus (string zip)
        {
            var client = new HttpClient();
            var msg = await client.GetAsync(string.Format(WeatherApiUrlBase, WeatherApiKey, zip));
            if (msg.IsSuccessStatusCode)
            {
                using (var stream = await msg.Content.ReadAsStreamAsync())
                {
                    using (var streamReader = new StreamReader(stream))
                    {
                        var str = await streamReader.ReadToEndAsync();
                        var obj = JsonConvert.DeserializeObject<WeatherStatus>(str);
                        return obj;
                    }
                }
            }
            return null;
        }

//FirstViewModel

public async override void Start ()
{
    base.Start ();
    FetchWeatherStatus ();
}

private async void FetchWeatherStatus() {
    var weatherData = await _service.GetWeatherStatus ("84043");
    FeelsLike = weatherData.current_observation.feelslike_f;
}

这个异步代码可以通过按下按钮 MvxCommand 正常工作;但是如果从构造函数、Init 或 Start 调用,如果我在那里没有断点,它每次都会冻结 UI。如果有人可以帮助我找到我做错了什么,我将不胜感激。谢谢。

编辑

我发现应用程序在我不调试时似乎运行正常。我从 Xamarin Studio 中停止调试器并重新启动应用程序,它运行良好。

这里要求的是我用于绑定 FeelsLike 的 XML:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    local:MvxBind="Text FeelsLike" />

我也尝试添加 .ConfigureAwait(false);对于我所有的等待电话,情况似乎更糟。 UI 永远不会被初始化,它会出现在黑屏上,而不仅仅是我的常规 UI 冻结。

这可能会有所帮助,但这是我的项目文件夹的链接(它非常少,因为我只是在学习这个框架): http://goo.gl/SJscZA

编辑 2

问题可能出在我的 HttpClient 代码中?我用一个简单的 Task.Delay(5000) 替换它并返回一个空的 WeatherStatus 以查看发生了什么并且它没有冻结。我将研究以另一种方式获取数据。我是 Xamarin 的新手,所以我愿意接受这里的建议。谢谢。这是我的 Task.Delay() 代码的 sn-p:

public async Task<WeatherStatus> GetWeatherStatus (string zip)
    {
        await Task.Delay (5000);
        return new WeatherStatus ();
    }

【问题讨论】:

  • 我看不出这段代码会导致挂起的任何原因。根据您的描述,这听起来像是活动的OnCreate/OnStart/OnResume 期间的竞争条件。您能否编辑您的问题以显示 FeelsLike 的 UI 绑定是什么样的? (我怀疑这会有所帮助 - 但我有希望!)作为一种潜在的调试方法,您可以尝试将跟踪添加到 VM 方法和 OnXXX 覆盖。对于变通方法,您可以使用 ConfigureAwait 将工作从 UI 线程中推送出去,或者您可以尝试将 DelayedStart 添加到 VM 并从 OnResume 调用它。

标签: c# android asynchronous xamarin mvvmcross


【解决方案1】:

通常,这意味着您需要将.ConfigureAwait(false) 添加到每个await,以防止同步上下文中的死锁:

    public async Task<WeatherStatus> GetWeatherStatus (string zip)
    {
        var client = new HttpClient();
        var msg = await client.GetAsync(string.Format(WeatherApiUrlBase, WeatherApiKey, zip)).ConfigureAwait(false);
        if (msg.IsSuccessStatusCode)
        {
            using (var stream = await msg.Content.ReadAsStreamAsync().ConfigureAwait(false))
            {
                using (var streamReader = new StreamReader(stream))
                {
                    var str = await streamReader.ReadToEndAsync().ConfigureAwait(false);
                    var obj = JsonConvert.DeserializeObject<WeatherStatus>(str);
                    return obj;
                }
            }
        }
        return null;
    }

【讨论】:

  • 谢谢马克。我添加了您的代码,不幸的是我看到了相同的行为;除了这次应用程序没有像以前那样发展。在此更改之前,它将加载 UI 并绑定我所有的硬编码值。现在它在我的初始屏幕后第一个 UI 加载之前停止。这是否提供任何线索?再次感谢。
  • @Grahambo 很有趣,但很抱歉:没有。我真的希望这会有所帮助:(
  • @Grahambo 你找到解决方案了吗?
猜你喜欢
  • 1970-01-01
  • 2015-09-11
  • 2018-01-05
  • 2014-08-08
  • 2014-09-02
  • 2022-01-09
  • 1970-01-01
  • 1970-01-01
  • 2022-06-12
相关资源
最近更新 更多