8

I am building an application using Xamarin (Android), it uses a PCL project as a Service layer. I have a Web Api endpoint and I am using HttpClient to consume it.

Everything works fine, but if I leave my Android app open and idle for a while (like 2 minutes) and I try to make a new request, the first request using the singleton HttpClient won't work. It just never returns and stays there until it timeouts (TaskCancelledException). I also put a breakpoint on my Api and it doesn't get hit. If I try to send the request again, then it works.

After a lot of debugging I found that this only happens if I try to use the HttpClient as a Singleton. If I create a new HttpClient for every request everything works.

At first I thought this was a deadlock issue, I've done a lot of research and double checked everything following the guidelines described in this other answer and Stephen Cleary's excellent post and I'm almost sure this is not the case.
I'm using ConfigureAwait(false) in every call from my PCL project so it doesn't capture the context.

The flow of a request goes like this:

Inside an Android Fragment:

SampleService svc = new SampleService();
response = await svc.GetAllSamples();

The service called (in my PCL project):

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;
        }
    }
}

This is how I build my Singleton instance:

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;
    }
}

I tried to simplify and isolate the code here, if I can provide any other useful snippets, just let me know.

Am I doing something wrong regarding singletons HttpClient that I'm not aware of?

Update: Just for clarification, I'm trying to use HttpClient as a Singleton just because, as I found in this answer by Darrel Miller and in the book Designing Evolvable Web APIs with ASP.NET (Chapter 14), they were designed to be reusable and thread-safe (in most of the cases). From my research I'm not using anything that is not thread-safe in it.

Community
  • 1
  • 1
Mahmoud Ali
  • 1,289
  • 1
  • 16
  • 31
  • 1
    Well you're not disposing of the responses, for one thing. I'm not sure whether HttpClient is *meant* to be used like this though... any reason you don't want to create a new one for each request? – Jon Skeet Sep 18 '15 at 16:26
  • At first I wasn't using it as a Singleton, but as Darrel Miller suggests, `HttpClient` can be reused (http://stackoverflow.com/questions/20074580/is-my-implementaton-of-httpclient-singleton-appropriate). Also stated in this book (http://shop.oreilly.com/product/0636920026617.do chapter 14). It doesn't actually say "Singleton", it just says it can be reused. – Mahmoud Ali Sep 18 '15 at 16:39
  • I'm not sure I need to dispose `Response` since every request is a new isolated object and it *should be* thread-safe. – Mahmoud Ali Sep 18 '15 at 16:45
  • 3
    `HttpResponseMessage` implements `IDisposable`, so you should dispose it. This may very well be the problem - it certainly would be for `HttpResponse`, where not disposing of a response would basically leave a connection in the connection pool "taken" until it was finalized. It's dead easy for you to dispose of the response here, so isn't it at least worth trying? – Jon Skeet Sep 18 '15 at 16:55
  • Thanks for the clarification @JonSkeet, I'll try this as soon as I get back and report here. – Mahmoud Ali Sep 18 '15 at 16:58
  • @JonSkeet I edited my answer disposing the response, I still get the same results – Mahmoud Ali Sep 18 '15 at 18:58
  • 2
    Coming back to the "Singleton": there is a chance that `HttpClient` does not work as expected on Android. It is a reimplementation using Mono and not the original .NET version. So why not give it a try and recreate the client if you need it? It really doesn't add a lot of overhead. 99% of all time will be spent in the actual request anyway, that's your bottleneck. If you find it behave differently on Mono vs. .NET, go ahead and file a bug at http://bugzilla.xamarin.com – Krumelur Sep 18 '15 at 19:06
  • 2
    [This](https://www.symbolsource.org/Public/Metadata/NuGet/Project/HttpClient/0.6.0/Release/.NETFramework,Version%3Dv4.0/System.Net.Http/System.Net.Http/System/Net/Http/HttpContent.cs?ImageName=System.Net.Http) is why you want to dispose the response message (check the `Dispose` method) – Yuval Itzchakov Sep 18 '15 at 19:12
  • I see @YuvalItzchakov, I edited my answer using dispose, unfortunately the issue persists – Mahmoud Ali Sep 18 '15 at 20:13
  • @MahmoudAli: Are you actually blocking on the http call at all? – Stephen Cleary Sep 19 '15 at 04:39
  • Yes @StephenCleary, I'm using `await` on my Fragment, and indeed the code is asynchronously waiting. I have a Progress Dialog showing that I omitted in the snippet just for clarity, it is shown while my call is hanging and it goes away on timeout. – Mahmoud Ali Sep 19 '15 at 10:45
  • 1
    I don't think the advice of re-using `HttpClient` is meant to be taken to such an extreme. It is not an expensive object to create; the benefits are related to re-using the underlying open connection, so if you if you hit the same host many times in a short period of time it may be more efficient to re-use the client. But if your app is sitting idle for 2 minutes between requests, I would absolutely re-instantiate the client. If your app is making relatively few requests in general, you should be just fine creating/disposing the client as needed. – Todd Menier Sep 19 '15 at 16:33
  • I'll probably stop using Singletons and I'll make sure I dispose my response objects, as suggested by you guys, but I'm still intrigued by the fact that this fails silently and I don't know the reason :( – Mahmoud Ali Sep 19 '15 at 21:31
  • 2
    As a side-note, your call to `JsonConvert.DeserializeObject` seems a little odd. *Usually* there's little point in calling `await Task.Run(...)` - it's CPU-bound work so you're not saving yourself a thead, and you're not doing anything in parallel. So it seems you're just switching threads needlessly there. – Todd Menier Sep 20 '15 at 14:34
  • Thanks for the reminder @ToddMenier – Mahmoud Ali Sep 20 '15 at 16:14
  • 1
    Was this ever resolved? – David Pine Dec 15 '15 at 19:36
  • I simply stopped using Singleton, but I did notice that if I ever forget to dispose a response, as Jon Skeet said, it will cause the hanging problem again. – Mahmoud Ali Dec 16 '15 at 14:37
  • @MahmoudAli can you link me for the full stack trace of the exception? – jzeferino Dec 02 '17 at 21:13
  • @jzeferino sorry, it's been so long, I don't have access to this codebase anymore. – Mahmoud Ali Dec 04 '17 at 12:31
  • PCL is deprecated, use .NET Standard instead. – Alsein Feb 29 '20 at 15:26

0 Answers0