async/await problem on Android - working on WindowsPhone

mapfmapf USMember

Hi,

I just started developing a cross-plattform app using Xamarin.Forms. I want to consume a rest service and do this using the following code:

            public async Task<bool> loginAsync(Uri uri, string user, string password)
            {

            var httpClientHandler = new HttpClientHandler();
                httpClient = new System.Net.Http.HttpClient(new HttpLoggingHandler(httpClientHandler));
                httpClient.BaseAddress = uri;

                FormUrlEncodedContent form = new FormUrlEncodedContent(new[] {
                    new KeyValuePair<string, string>("username", user),
                    new KeyValuePair<string, string>("password", password)
                });

                try
                {
                    var httpResponse = await httpClient.PostAsync("login", form).ConfigureAwait(false);
                    validateResponse(httpResponse);

                    string loginResult = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
                }
                catch (Exception e)
                {
                    throw new CommunicationException("Unable to login", e);
                }
    }

This code runs perfectly fine on windows phone. However, when I start it on android, the http request is sent but I don't receive a response. the debugger also does not continue beyond the line of the await.

Also the caller of this method is an async command, so it should also not be the deadlock issue when we wait synchronously for an async method. Here is the code that calls this method:

LoginCommand = new Command(async() => { try { LoggedIn = await App.Communication.loginAsync(new UriBuilder(UriString).Uri, Username, Password); if (LoggedIn) { Output = "Login successful!"; } } } catch (CommunicationException e) { //set the message to show the data typed into the text editor Output = e.ToString(); } });

Does anyone have an idea on this?

Posts

  • ThomasLoidoltThomasLoidolt ATMember ✭✭

    So you don't get any exception?

  • mapfmapf USMember
    edited June 2015

    No I don't. I see the HTTP request that is logged to the debug view. Afterwards I get the following output in the debug view:
    06-30 13:42:49.956 D/Mono ( 1160): Assembly Ref addref Mono.Android[0x24f198] -> System[0x456f60]: 12 FIX ME! implement getprotobyname() bionic/libc/bionic/stubs.c:450 FIX ME! implement getprotobyname() bionic/libc/bionic/stubs.c:450 FIX ME! implement getprotobyname() bionic/libc/bionic/stubs.c:450 Thread started: <Thread Pool> #6 Thread started: <Thread Pool> #7 Thread started: <Thread Pool> #8 06-30 13:42:51.807 D/Mono ( 1160): DllImport attempting to load: '__Internal'. 06-30 13:42:51.807 D/Mono ( 1160): DllImport loaded library '(null)'. 06-30 13:42:51.817 D/Mono ( 1160): DllImport searching in: '__Internal' ('(null)'). 06-30 13:42:51.817 D/Mono ( 1160): Searching for 'monodroid_get_system_property'. 06-30 13:42:51.817 D/Mono ( 1160): Probing 'monodroid_get_system_property'. 06-30 13:42:51.817 D/Mono ( 1160): Found as 'monodroid_get_system_property'. 06-30 13:42:51.817 D/Mono ( 1160): DllImport searching in: '__Internal' ('(null)'). 06-30 13:42:51.817 D/Mono ( 1160): Searching for 'monodroid_free'. 06-30 13:42:51.817 D/Mono ( 1160): Probing 'monodroid_free'. 06-30 13:42:51.827 D/Mono ( 1160): Found as 'monodroid_free'. Thread finished: <Thread Pool> #5 Thread finished: <Thread Pool> #5 The thread 'Unknown' (0x5) has exited with code 0 (0x0).

    In the debugger when I do a step over of the await PostAsync line it does not return. I think its related to some kind of threading topic. I am not experienced using the async/await keywords. I read some articles and think I understand the basics. What makes me wonder a bit is that it seems to work fine on Windows Phone, but does not on Android. As I have no iPhone I cannot check the iOS version...

    If I wait long enough I get a "TaskCanceledException" from the CompilerServices.TaskAwaiter. So I guess this is because of a timeout that happens.

  • ThomasLoidoltThomasLoidolt ATMember ✭✭

    OK, the TaskCanceledException is the key :) I had the same problem a view days ago and that's true that if you get a TaskCanceledException from the HttpClient it's usually a timeout exception.
    Just increase the timeout in the HttpClient and look what happens.

  • ErosSteinErosStein BRMember

    @mapf Hey, how were you able to use System.Net.Http with WP? I'm having a different problem than yours... I can use System.Net.Http with Android and iOS and they work perfectly, but if I try to build the WP project it doesn't recognize the DLL.

  • mapfmapf USMember

    The timeout should not be the problem. I think it lasts around 1min until I get the exception. However, the rest of our server applications responds immediately. Also in the WindowsPhone version I have the result in less than 1 second...

    I would rather guess that it is related to the async handling. Something like the result would be there but there is no thread that reads it or something like that.

    @ErosStein I also struggled with this. I think I had to install the "Microsoft HTTP Client Libraries" using NuGet to be able to use it.

  • ErosSteinErosStein BRMember

    @mapf I think I've tried that already, but I'll check that again... BTW, this is the code I use for my REST requests (in case you wanna check if it helps you):

    public static async Task<T> GetAsync<T>(this HttpClient client, string url, string parameters = "")
    {
        var httpRequest = new HttpRequestMessage(new HttpMethod("GET"),
        string.Format("{0}{1}", url, string.IsNullOrEmpty(parameters) ? "" : Uri.EscapeDataString(parameters)));
    
        client.Timeout = TimeSpan.FromSeconds(30);
    
        var response = await client.SendAsync(httpRequest);
        var jsonString = await response.Content.ReadAsStringAsync();
        var result = JsonConvert.DeserializeObject<T>(jsonString);
    
        return result;
     }
    

    and this is how I call the extension:

    var httpClient = new HttpClient(new NativeMessageHandler()); return await httpClient.GetAsync<MyCustomResponse>(string.Format("https://mysite.com/api/account/login?usernameOrEmail={0}&password={1}", usernameOrEmail, password));

    I'm using ModernHttp.

  • ThomasLoidoltThomasLoidolt ATMember ✭✭

    Maybe you can try it without the configureawait(false) so that it will executed in the same context...
    But the permissions in the android-manifest are activated to have access to the internet?

  • mapfmapf USMember

    I just found out something interesting. When I use this content:
    HttpContent content = new StringContent("test");

    instead of this content:
    FormUrlEncodedContent form = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("username", user), new KeyValuePair<string, string>("password", password) });
    I get a response. What I also found out, when I put the server application to debug mode and step through it, when the server sends the response, the mobile app logs the following:
    Thread finished: <Thread Pool> #5 Thread finished: <Thread Pool> #5 The thread '<Thread Pool>' (0x5) has exited with code 0 (0x0).

    So it seems that on the mobile app the response is received so the thread finishes. The only thing that I could think of is that the response that is sent when I use the correct content has something in it that causes the problem.

    Because the StringContent is not expected on the serverside and I get a redirect to a login screen. I get this without problems. When I send the Form content then the login on the server is successful and I get a different response.

    I will try to send the same response even with a successful login to see if the problem is caused by the FormUrlEncodedContent or simply by the delivered content.

  • ThomasLoidoltThomasLoidolt ATMember ✭✭

    Ok, there is my working solution for this topic maybe it helps.

     using (var client =  GetConfiguredHttpClient())
                    {
    
    
    
                        var content = new StringContent(dataBody, Encoding.UTF8, "text/xml");
                        var url = Settings.AppSettings.GetValueOrDefault<string>(Settings.UrlKey);
                        HttpResponseMessage msg =   await client.PostAsync(new Uri(url), content).ConfigureAwait(false);
    
    
                        XElement xData = ParseSoapResponse(await msg.Content.ReadAsStringAsync().ConfigureAwait(false));
    
                        ....
                        }
    
                      .....
    
                    }
    
    
    
    private HttpClient GetConfiguredHttpClient()
            {
                var byteArray = Encoding.UTF8.GetBytes(User + ":" + Password);
    
                HttpClient client = new HttpClient();
    
                client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
                client.DefaultRequestHeaders.Add("xyz", "zyx");
                client.Timeout = new TimeSpan(0,0,15);
    
                return client;
    
            }
    
  • mapfmapf USMember

    Thanks, but I don't think anymore that it is a problem with the async stuff. I found out the following:

    • When I send a request to URL A the server responds with a response code 200
      --> Windows Phone: Works, I get the response 200 with the content
      --> Android: Works, I get the response 200 with the content

    • When I send a request to URL B the server responds with a response code 302 which means a temporary redirect
      --> Windows Phone: Works, I immediately get a response 200 of the redirect target. I cannot see the response 302 in the HTTPCLient (but I see it in browser development tools)
      --> Android: Does not work. I get no response, so no 302 and no 200 but I get the timeout

    I will research a bit on this redirect topic for the HTTPClient. Do you know, does the HTTPClient itself handle the redirect internally?

  • mapfmapf USMember

    And I think I have the same problem as mentioned here: https://forums.xamarin.com/discussion/42410/bug-httpclient-times-out-when-redirected-on-a-post-request

    Seems it is a mono bug that is already known for quite some time (https://bugzilla.xamarin.com/show_bug.cgi?id=22384). Probably I will have to handle the redirect myself and disable auto redirecting

    Thanks for your help

Sign In or Register to comment.