C# Xamarin 没有收到来自 HttpClient 的响应
Posted
技术标签:
【中文标题】C# Xamarin 没有收到来自 HttpClient 的响应【英文标题】:C# Xamarin does not receive response from HttpClient 【发布时间】:2020-07-20 18:42:35 【问题描述】:我在 C# Xamarin 中有以下方法进行身份验证:
private async Task<HttpResponseMessage> _SendAsync(HttpMethod method, string url, object obj)
HttpRequestMessage request = new HttpRequestMessage(method, url);
request.Content = new StringContent(JsonConvert.SerializeObject(obj), Encoding.UTF8, "application/json");
if (!string.IsNullOrEmpty(this._AuthToken))
request.Headers.Add("Authorization", this._AuthToken);
return await this._Client.SendAsync(request);
服务器收到请求,认证成功(出现在服务器日志中)。基于wireshark,许多响应从服务器返回,但函数没有返回。应用程序冻结。
有什么想法吗? 此方法在普通控制台客户端上运行没有任何问题。
没有错误信息,只是一个冻结的客户端。
谢谢你的回答!
【问题讨论】:
你怎么称呼_SendAsync
?另外,为什么不直接从SendAsync
返回任务,而不是在不需要立即等待时等待它呢?只需从签名和等待中删除 async 关键字。产量,类似的结果,只是包装更少。但我认为问题出在您实际调用_SendAsync
的代码中,它可能被称为错误并且无法返回它来自的上下文,从而导致死锁。
this._Client 是 HttpClient 对象的一个实例。我忘记写了,因为名字是一样的。 HttpClient 只有 SendAsync 方法,但我会尝试。
@Cheesebaron:你能评论为答案吗?我想接受,但如果它只是评论,我不能接受。谢谢
【参考方案1】:
您在这里遇到的是典型的死锁案例。在 cmets 中,我建议简单地删除 async
和 await
关键字并简单地返回 Task
HttpClient.SendAsync
方法返回。为什么这会改变什么?让我解释一下。
就在你之前await
一个异步方法捕获当前的SynchronizationContext
。在您的情况下,它可能是 UI 线程。
然后当实际的SendAsync
方法完成它的工作时,它会尝试返回到那个SynchronizationContext
。这通常很好。但是,这实际上取决于您实际调用封装方法的方式。
如果您从 UI 线程执行以下操作:_SendAsync(..).Result
,这很可能会导致死锁。由于.Result
是一个阻塞调用,UI 线程将等待.Result
完成。由于 UI 线程正在等待,在等待的 SendAsync
方法完成后返回 UI 线程 SynchronizationContext
将不起作用,因为 UI 线程被阻塞。
那么在这种情况下,简单地返回任务有什么作用呢?由于您没有在等待 Task 并且该方法没有标记为异步,因此上下文切换的责任被向上移动到调用 _SendAsync
的位置。在此处调用 .Result
不会进行上下文切换,而只是进行阻塞调用,并失去执行 async/await 的所有好处。
或者,您也可以将.ConfigureAwait(false)
添加到等待的this._Client.SendAsync()
调用中。这样做的目的是不返回捕获 SynchronizationContext
,而只是返回执行它的任何 ThreadPool 线程。
在任何一种情况下,使用.Result
或.GetAwaiter().GetResult()
时都要非常小心,在大多数情况下应该避免使用。
通常应该等待任务。或者,如果您并不真正关心结果,您可以使用 Task.Run()
解雇并忘记。
大多数不需要返回捕获的上下文的服务调用和异步调用,大部分都可以使用ConfigureAwait(false)
进行后置修复。没有它的主要原因是如果您想在之后更新 UI。
您可以阅读更多关于ConfigureAwait
in the FAQ by Stepen Toub的信息
我强烈建议您阅读有关 async/await in the MS Docs 的优秀文档
TL;DR:
要么做:
private async Task<HttpResponseMessage> _SendAsync(HttpMethod method, string url, object obj)
HttpRequestMessage request = new HttpRequestMessage(method, url);
request.Content = new StringContent(JsonConvert.SerializeObject(obj), Encoding.UTF8, "application/json");
if (!string.IsNullOrEmpty(this._AuthToken))
request.Headers.Add("Authorization", this._AuthToken);
return await this._Client.SendAsync(request).ConfigureAwait(false);
或者做:
private Task<HttpResponseMessage> _SendAsync(HttpMethod method, string url, object obj)
HttpRequestMessage request = new HttpRequestMessage(method, url);
request.Content = new StringContent(JsonConvert.SerializeObject(obj), Encoding.UTF8, "application/json");
if (!string.IsNullOrEmpty(this._AuthToken))
request.Headers.Add("Authorization", this._AuthToken);
return this._Client.SendAsync(request);
解决问题。
并避免在 Tasks 上调用 .Result
。永远等待他们。
【讨论】:
以上是关于C# Xamarin 没有收到来自 HttpClient 的响应的主要内容,如果未能解决你的问题,请参考以下文章
来自 ios 中的 Visual Studio xamarin 的 Web 服务调用
有没有办法在 Xamarin C# 中的 ListView 的列上方创建列名
使用来自 Visual Studio (Xamarin) 的 Google Play 商店签名始终收到“无效客户端”错误