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