我正在使用Xamarin(Android)构建应用程序,它使用PCL项目作为服务层。我有一个Web Api端点,我使用HttpClient
来使用它。
一切正常,但是如果我让我的Android应用程序打开并闲置一段时间(比如2分钟)并且我尝试发出新请求,那么使用单例HttpClient
的第一个请求将无效。它永远不会返回并保持在那里直到超时(TaskCancelledException
)。我也在我的Api上设置了一个断点,它没有被击中。如果我再次尝试发送请求,那么它可以正常工作。
经过大量调试后,我发现只有当我尝试将HttpClient
用作Singleton时才会发生这种情况。如果我为每个请求创建一个新的HttpClient
,一切正常。
起初我认为这是一个死锁问题,我做了很多研究,并按照other answer和Stephen Cleary's excellent post中描述的指导原则仔细检查了一切,我几乎可以肯定这是不是这样的。
我在PCL项目的每个调用中使用ConfigureAwait(false)
,因此它不捕获上下文。
在Android片段中:
SampleService svc = new SampleService();
response = await svc.GetAllSamples();
该服务(在我的PCL项目中):
public class SampleService
{
public HttpClient Client { get; set; }
public SampleService()
{
// resolves my singleton instance and uses my custom DelegatingHandler
Client = CustomHttpClient.Instance;
}
public async Task<IEnumerable<Sample>> GetAllSamples()
{
IEnumerable<Sample> list = null;
// this never returns and timeouts the first time
using (var response = await Client.GetAsync("samples").ConfigureAwait(false))
{
if (response.IsSuccessStatusCode)
{
string json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
lista = await Task.Run(() => JsonConvert.DeserializeObject<IEnumerable<Sample>>(json)).ConfigureAwait(false);
}
return list;
}
}
}
这就是我构建Singleton实例的方法:
public sealed class CustomHttpClient
{
private static HttpClient _client;
public static HttpClient GetClient()
{
if (_client == null)
{
HttpMessageHandler messageHandler = new HttpClientHandler();
_client = new HttpClient(messageHandler);
_client.Timeout = TimeSpan.FromSeconds(30);
_client.BaseAddress = new Uri("myendpoint");
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
return _client;
}
}
我试图在这里简化和隔离代码,如果我可以提供任何其他有用的代码段,请告诉我。
我是否对单身HttpClient
做了一些我不知道的事情?
更新:为了澄清,我正在尝试将HttpClient
用作单身人士,正如我在this answer by Darrel Miller和书中{{3}中找到的那样(第14章),它们被设计为可重用且线程安全(在大多数情况下)。根据我的研究,我没有使用任何非线程安全的东西。