Resolving Native HttpClient Exceptions - Android

Adrian Jakubcik 111 Reputation points
2022-02-04T21:36:47.13+00:00

Hello, I'd like to ask if there is a way to catch unhandled native exceptions on android in a shared code... Because currently I made a class which is making http requests using HttpClient and everything was working perfectly until I tried to use my app without an internet connection, that's when my android app started crashing so I debugged the exception and it seems like it throws a native Java.Net.UnknownHostException which my try{} catch(){} is unable to handle.

public static async Task<string> Get(string Url, Dictionary<string, object> Arguments, CancellationToken Token)
        {
            try
            {
                HttpClient client = new HttpClient();
                string finalUrl = Url;
                if (Arguments != null && Arguments.Count > 0)
                {
                    finalUrl = UrlCompiler(Url, Arguments);
                }
                var response = client.GetAsync(finalUrl, HttpCompletionOption.ResponseContentRead, Token).GetAwaiter().GetResult();
                return await response.Content.ReadAsStringAsync();
            }
            catch(Exception er)
            {
                Console.WriteLine("Error thrown: " + er.ToString());
                return null;
            }
        }

And whenever I call this function with no connection to the internet my app crashes on android with this stack trace.

[monodroid] Not wrapping exception of type Java.Net.UnknownHostException from method `SendAsync`. This will change in a future release.
[InputMethodManager] HSIFW - flag : 0
Error thrown: Java.Net.UnknownHostException: Unable to resolve host "google.com": No address associated with hostname ---> Java.Lang.RuntimeException: android_getaddrinfo failed: EAI_NODATA (No address associated with hostname)
   --- End of inner exception stack trace ---
  at Java.Interop.JniEnvironment+InstanceMethods.CallVoidMethod (Java.Interop.JniObjectReference instance, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x0006e] in <00c315a988634383b446eff646084784>:0 
  at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeAbstractVoidMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x00014] in <00c315a988634383b446eff646084784>:0 
  at Javax.Net.Ssl.HttpsURLConnectionInvoker.Connect () [0x00000] in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/obj/Release/monoandroid10/android-30/mcw/Javax.Net.Ssl.HttpsURLConnection.cs:433 
  at Xamarin.Android.Net.AndroidClientHandler+<>c__DisplayClass44_0.<ConnectAsync>b__0 () [0x0007d] in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.Legacy.cs:356 
  at System.Threading.Tasks.Task.InnerInvoke () [0x0000f] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs:2476 
  at System.Threading.Tasks.Task.Execute () [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs:2319 
--- End of stack trace from previous location where exception was thrown ---
  at Xamarin.Android.Net.AndroidClientHandler.DoProcessRequest (System.Net.Http.HttpRequestMessage request, Java.Net.URL javaUrl, Java.Net.HttpURLConnection httpConnection, System.Threading.CancellationToken cancellationToken, Xamarin.Android.Net.AndroidClientHandler+RequestRedirectionState redirectState) [0x000e4] in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.Legacy.cs:405 
  at Xamarin.Android.Net.AndroidClientHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) [0x00286] in /Users/builder/azdo/_work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.Legacy.cs:287 
  at System.Net.Http.HttpClient.FinishSendAsyncBuffered (System.Threading.Tasks.Task`1[TResult] sendTask, System.Net.Http.HttpRequestMessage request, System.Threading.CancellationTokenSource cts, System.Boolean disposeCts) [0x0017e] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpClient.cs:506 
  at Mobile_App.Modules.Request.Get (System.String Url, System.Collections.Generic.Dictionary`2[TKey,TValue] Arguments, System.Threading.CancellationToken Token) [0x00092] in repos\Mobile App\Mobile App\Mobile App\Modules\Request.cs:190 
  --- End of managed Java.Net.UnknownHostException stack trace ---

Obviously, I can't just let this be and wait for a future releases that may change this, so I searched the internet for the most efficient workaround and found one guy saying that you could create a custom AndroidClientHandler and catch all the exceptions we want. I tried doing so and extended the Handler yet I don't know how to use it in my shared code since it is in my android project.

namespace Mobile_App.Droid
{
    public class CustomAndroidClientHandler : AndroidClientHandler
    {
        public CustomAndroidClientHandler()
        {
            this.AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate; 
        }

        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            // The native Android HTTP client handler bubbles up Java exceptions in contrast to the iOS native handler.
            // See for instance https://github.com/xamarin/xamarin-android/issues/3216.
            // We see this behavior on a Samsung 9 with Android 9.
            // Remedy is to explicitly catch expected native network exceptions.
            try
            {
                return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
            }
            catch (Java.Net.UnknownHostException ex)
            {
                throw new NoConnectionException($"Native UnknownHostException on HTTP request: {ex.Message}", ex);
            }
            catch (Java.Net.SocketException ex)
            {
                throw new WebException($"Native SocketException on HTTP request: {ex.Message}", ex);
            }
            catch (Java.IO.IOException ex)
            {
                throw new WebException($"Native IOException on HTTP request: {ex.Message}", ex);
            }
            catch (Java.Lang.Exception ex)
            {
                throw new WebException($"Native Exception on HTTP request: {ex.Message}", ex);
            }
        }
    }
}
Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,326 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,648 questions
0 comments No comments
{count} votes

Accepted answer
  1. Leon Lu (Shanghai Wicresoft Co,.Ltd.) 72,251 Reputation points Microsoft Vendor
    2022-02-07T08:09:19.277+00:00

    Hello,​

    Welcome to our Microsoft Q&A platform!

    I tried doing so and extended the Handler yet I don't know how to use it in my shared code since it is in my android project.

    You can create a static object in the App.xaml.cs like this code.

       public static object HttpClientHandler;  
    

    Then open your MainActivity.cs to initialize CustomAndroidClientHandler in the OnCreate method, put it before the LoadApplication(new App()); line

       protected override void OnCreate(Bundle savedInstanceState)  
               {  
                   TabLayoutResource = Resource.Layout.Tabbar;  
                   ToolbarResource = Resource.Layout.Toolbar;  
         
                   base.OnCreate(savedInstanceState);  
         
                   Xamarin.Essentials.Platform.Init(this, savedInstanceState);  
                   global::Xamarin.Forms.Forms.Init(this, savedInstanceState);  
         
                   var androidClientHandler = new CustomAndroidClientHandler();  
                   App.HttpClientHandler = androidClientHandler;  
         
                   LoadApplication(new App());  
               }  
    

    Then you can use App.HttpClientHandler in your shared code when you new HttpClient().

       new HttpClient((HttpMessageHandler)App.HttpClientHandler)  
                   {  
         
                   };  
    

    Best Regards,

    Leon Lu


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


0 additional answers

Sort by: Most helpful