Xamarin iOS应用中await调用无响应但使用.Result可正常运行
Xamarin iOS MVVMCross 异步请求UI卡死问题解决方案
根因分析
这是典型的UI同步上下文死锁问题,与MVVMCross框架的ViewModel.Initialize执行逻辑、async/await上下文捕获机制直接相关:
- MVVMCross旧版本中,
Initialize方法默认被调度到UI同步上下文执行,且框架本身会通过.Wait()/.Result阻塞等待Initialize异步任务完成 - 当你使用不带*ConfigureAwait(false)*的await时,await结束后会尝试回到已被阻塞的UI上下文执行后续代码,形成「框架等任务完成、await后代码等UI上下文释放」的死锁,所以await后的断点无法命中,UI也会卡住空白
- 使用*.Result*能运行是因为直接在当前上下文同步执行请求,没有上下文切换过程,不会触发死锁,但该写法会阻塞UI线程,请求时间过长会触发系统无响应判定
- 加*ConfigureAwait(false)*后能命中后续断点,是因为await结束后不会回到原UI上下文,直接在线程池线程执行后续代码,死锁被解开,但如果后续代码直接操作UI绑定数据源,未切回UI线程就会导致UI更新异常,出现空白
补充提示:你提供的首段示例代码中
GetStryngAsync为拼写错误,实际开发需修正为GetStringAsync
修复方案
可按实际开发情况选择以下方案:
- 优先升级MVVMCross到最新稳定版:新版本已修复异步Initialize的死锁问题,框架不再阻塞UI线程等待Initialize完成,原有await写法可直接正常运行
- 若暂不能升级框架,将异步请求逻辑从
Initialize迁移到ViewAppeared等生命周期方法中执行,更新UI数据源前手动切回UI线程:
public override async void ViewAppeared() { base.ViewAppeared(); var client = new HttpClient(); var res = await client.GetStringAsync("https://myurl.com"); // 切回UI线程更新绑定数据 InvokeOnMainThread(() => { DataList = JsonConvert.DeserializeObject<List<MyModel>>(res); }); }
- 若必须保留在Initialize中实现异步逻辑,添加
ConfigureAwait(false)后,所有UI绑定数据的更新操作都手动调度到UI线程执行即可解决空白问题:
public override async Task Initialize() { await base.Initialize(); var client = new HttpClient(); var res = await client.GetStringAsync("https://myurl.com").ConfigureAwait(false); var data = JsonConvert.DeserializeObject<List<MyModel>>(res); // 手动切回UI线程更新绑定数据 Mvx.IoCProvider.Resolve<IMvxMainThreadAsyncDispatcher>() .ExecuteOnMainThreadAsync(() => { DataList = data; }); }
- 额外优化建议:HttpClient建议使用单例模式,不要每次请求都新建实例,避免套接字资源耗尽的问题。
内容的提问来源于stack exchange,提问作者Gabriel Ferreiro
相关产品推荐
相关产品推荐

