-
Notifications
You must be signed in to change notification settings - Fork 527
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HttpClient ObjectDisposed after SDK upgrade from 34.0.95 -> 34.0.113 #9039
Comments
@simonrozsival Now that we are part of the |
@jpobst this might be an issue in |
I forgot to mention another work around I've had success with:
This might end up with throwing another dispose exception, or not. I've added a 5 second retry, which will end up with a success at some point during those 5 seconds. If you are interested: internal static class SharedRetryHelper
{
internal static Task<HttpResponseMessage> RetryDueToAndroidBug(this IHttpClient httpClient, HttpRequestMessage requestMessage, CancellationToken cancellationToken,ObjectDisposedException objectDisposedException)
{
var dateTimeNow = DateTime.Now;
var now = new TimeOnly(dateTimeNow.Hour, dateTimeNow.Minute, dateTimeNow.Second);
if (requestMessage.RequestUri == null) throw objectDisposedException;
if (httpClient.DisposeInformation.LastTimeDisposed != null)
{
var nowTicks = DateTime.Now.Ticks;
var lastTime = httpClient.DisposeInformation.LastTimeDisposed.Value;
if (lastTime.Add(TimeSpan.FromSeconds(5)).Ticks > nowTicks) //If it's been more than 5seconds of disposal exceptions.
{
httpClient.DisposeInformation.LastTimeDisposed = null;
Log(httpClient, requestMessage, "Got disposed exception on Android for more than 5 seconds. Will give up and throw exception.");
throw objectDisposedException;
}
}
else
{
httpClient.DisposeInformation.LastTimeDisposed = now;
}
httpClient.DisposeInformation.TotalTimesObjectDisposed++;
Log(httpClient, requestMessage, $"Got disposed exception on Android. Has been disposed {httpClient.DisposeInformation.TotalTimesObjectDisposed} times, last time was {httpClient.DisposeInformation.LastTimeDisposed.Value}:{httpClient.DisposeInformation.LastTimeDisposed.Value.Second}, will retry now at {now}:{now.Second}");
if (requestMessage.Method == HttpMethod.Get)
{
return httpClient.Get(requestMessage.RequestUri.AbsoluteUri, cancellationToken);
}
if (requestMessage.Method == HttpMethod.Post)
{
return httpClient.Post(requestMessage.RequestUri.AbsoluteUri, requestMessage.Content, cancellationToken);
}
if (requestMessage.Method == HttpMethod.Put)
{
return httpClient.Put(requestMessage.RequestUri.AbsoluteUri, requestMessage.Content, cancellationToken);
}
if (requestMessage.Method == HttpMethod.Delete)
{
return httpClient.Delete(requestMessage.RequestUri.AbsoluteUri, cancellationToken);
}
throw objectDisposedException;
}
private static void Log(IHttpClient httpClient, HttpRequestMessage requestMessage, string message)
{
#if __ANDROID__
if (requestMessage.RequestUri != null)
{
Android.Util.Log.Debug("DME HttpClientAdapter",$"HttpClientName: {httpClient.Name} : {requestMessage.RequestUri.AbsoluteUri} : {message}");
}
#endif
}
|
@simonrozsival: Just shout out if you need me to test potential nightly builds or something in our project. |
@haavamoa can you please share more information about the settings you're using with the client? I'm especially interested in automatic decompression, built-in authentication (Basic?, NTLM?), proxies, ... Also are you able to tell what happened with the request just before it threw the exception (redirect, 4xx error, 5xx error)? Does this happen for a specific HTTP method or does it happen both with and without any body sent to the server? |
Hi @simonrozsival. We've seen this bug on both HttpClients that is using authentication, but also on HttpClients which his not. For the ones that do ; we are using built-in-authentication using Bearer Token from the Identity Model flow. We've not set any decompression or proxies to our HttpClients. No requests have failed before we do the calls. One example is simply trying to reach a /status/ping endpoint which does nothing more than return 200 OK. This fail sporadicly. It happens for all kinds of HTTP methods, with or without bodies. I've spent days trying to see the connection between the order of doing calls, or what happens to them before it fails, but I am unable to find a pattern at all. From my point of view it can happen to any call we do regardless of the situation, which I find too good to be true to be honest. |
Thanks for the details, @haavamoa. I remember seeing reports of If you think of any additional details (are you making requests in parallel or just one at a time, you're making requests always when the app is in foreground or if the app is in background, ...) or if you're able to repro it reliably, please let me know. |
@haavamoa could you try building your app with |
Hey @simonrozsival, I have started observing this exception randomly in our http requests (MAUI) after the tooling was upgraded to Android SDK 34.0.113. Looking through our logs I do not see any discernable patterns for when the failure happens. |
@zachdean do you have any stacktraces you can share? Are you able to reproduce the exception in your app? |
Same happens for us after workload was updated to 34.0.113 As a workaround we will try this: Make a file like this named Stacktrace of the crash:
|
@zachdean do you also use Polly? |
@simonrozsival , adding |
@haavamoa can you share a stacktrace from the app with |
@simonrozsival we are not using poly, but we do have a retry mechanism that is in the request pipeline. We are injecting System.ObjectDisposedException: ObjectDisposed_Generic ObjectDisposed_ObjectName_Name, Java.IO.InputStreamInvoker |
So, we don't see any changes to
Is it possible this actually changed the runtime version? If you have a rollback file like: {
"microsoft.net.sdk.android": "34.0.95/8.0.100"
} I don't actually know what it would choose to do with the runtime. Did it downgrade the runtime to 8.0.0? If you have a |
The commit diff between 34.0.95 and 34.0.113 is: 34.0.95...34.0.113 There is only one change to That's all I can say with any degree of confidence. :-) I have the same question as @jonathanpeppers does. @simonrozsival: meanwhile, within the dotnet/android change set, we also changed the dotnet/runtime that we use for unit tests in 784d320, for a runtime commit diff of: dotnet/runtime@62304a6...fd8f5b5 which does contain I'm thus inclined to believe that something may have changed on the dotnet/runtime side. |
I will see if I can provide a .binlog file. |
Here's my binlog from the build where I was running the following command and setup:
|
@simonrozsival , this is the stack trace of the error with the following setup:
and
Stack trace: at Java.Interop.JniPeerMembers.AssertSelf(IJavaPeerable self) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.cs:line 153
at Java.Interop.JniPeerMembers.JniInstanceMethods.InvokeVirtualVoidMethod(String encodedMember, IJavaPeerable self, JniArgumentValue* parameters) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods_Invoke.cs:line 57
at Java.IO.InputStream.Close() in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net8.0/android-34/mcw/Java.IO.InputStream.cs:line 116
at Android.Runtime.InputStreamInvoker.Close() in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/InputStreamInvoker.cs:line 62
at System.IO.Stream.Dispose()
at System.IO.BufferedStream.Dispose(Boolean disposing)
at System.IO.Stream.Close()
at System.IO.Stream.Dispose()
at System.Net.Http.StreamContent.Dispose(Boolean disposing)
at System.Net.Http.HttpContent.Dispose()
at System.Net.Http.HttpResponseMessage.Dispose(Boolean disposing)
at Xamarin.Android.Net.AndroidHttpResponseMessage.Dispose(Boolean disposing) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Xamarin.Android.Net/AndroidHttpResponseMessage.cs:line 42
at System.Net.Http.HttpResponseMessage.Dispose()
at System.Net.Http.HttpClient.HandleFailure(Exception e, Boolean telemetryStarted, HttpResponseMessage response, CancellationTokenSource cts, CancellationToken cancellationToken, CancellationTokenSource pendingRequestsCts)
at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken) |
@haavamoa from your
In the case you've gotten it to work by installing with a rollback file, is your build using a different runtime?
|
@haavamoa @jonathanpeppers given the last stacktrace, it is not using the managed handler, but it is still using the native one (notice |
I'm still musing over the above data sharing hypothesis, and I'm less convinced that it explains this issue. Consider the namespace Java.Net {
public abstract partial class URLConnection : Java.Lang.Object {
public virtual unsafe System.IO.Stream? InputStream {
// Metadata.xml XPath method reference: path="/api/package[@name='java.net']/class[@name='URLConnection']/method[@name='getInputStream' and count(parameter)=0]"
[Register ("getInputStream", "()Ljava/io/InputStream;", "GetGetInputStreamHandler")]
get {
const string __id = "getInputStream.()Ljava/io/InputStream;";
try {
var __rm = _members.InstanceMethods.InvokeVirtualObjectMethod (__id, this, null);
return Android.Runtime.InputStreamInvoker.FromJniHandle (__rm.Handle, JniHandleOwnership.TransferLocalRef);
} finally {
}
}
}
}
}
android/src/Mono.Android/Android.Runtime/InputStreamInvoker.cs Lines 156 to 169 in 3ab04df
Which, upon review with fresh eyes, implicitly causes data sharing! Untested but looks quite plausible: var a = urlConnection.InputStream;
var b = urlConnection.InputStream;
// I don't see how this could fail, due to semantics of `InputStreamInvoker.FromJniHandle()`
Assert.AreNotSame (a, b);
// This should succeed, because the `URLConnection.getInputStream()` isn't going to create a new
// instance on every invocation…
Assert.AreSame(a.BaseInputStream, b.BaseInputStream);
a.Dispose(); // fine
b.Dispose(); // boom w/o #9103 …which kinda makes me feel bad. That said, there's no evidence that's happening here: My guess is that #9103 will "fix" the problem, in that the |
Context: #9039 Context: #9039 (comment) Context: 0315e89 In #9039, a customer reports that starting with the .NET for Android workload 34.0.113, their app would start crashing with an `ObjectDisposedException`: ObjectDisposed_ObjectName_Name, Java.IO.InputStreamInvoker at Java.Interop.JniPeerMembers.AssertSelf(IJavaPeerable ) at Java.Interop.JniPeerMembers.JniInstanceMethods.InvokeVirtualVoidMethod(String , IJavaPeerable , JniArgumentValue* ) at Java.IO.InputStream.Close() at Android.Runtime.InputStreamInvoker.Close() at System.IO.Stream.Dispose() at System.IO.BufferedStream.Dispose(Boolean ) at System.IO.Stream.Close() at System.IO.Stream.Dispose() at System.Net.Http.StreamContent.Dispose(Boolean ) at System.Net.Http.HttpContent.Dispose() at System.Net.Http.HttpResponseMessage.Dispose(Boolean ) at Xamarin.Android.Net.AndroidHttpResponseMessage.Dispose(Boolean ) at System.Net.Http.HttpResponseMessage.Dispose() at System.Net.Http.HttpClient.HandleFailure(Exception , Boolean , HttpResponseMessage , CancellationTokenSource , CancellationToken , CancellationTokenSource ) at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage , HttpCompletionOption , CancellationTokenSource , Boolean , CancellationTokenSource , CancellationToken ) This was rather confusing, as between [34.0.95 and 34.0.113][0] the only change to `Mono.Android.dll` was 0315e89, which has nothing to do with networking. Additional consideration presented a hypothetical: [`IDisposable.Dispose()` should be idempotent][1]: > To help ensure that resources are always cleaned up appropriately, > a Dispose method should be idempotent, such that it's callable > multiple times without throwing an exception. *Is* `InputStreamInvoker.Dispose()` idempotent? An additional conundrum is that `InputStreamInvoker.Dispose()` doesn't exist; it's `Stream.Dispose()` that exists, and [`Stream.Dispose()` invokes `Stream.Close()`][2]. This in turn means that `Stream.Close()` must be idempotent, which in turn means that `InputStreamInvoker.Close()` must be idempotent. Additionally, [`Stream.Close()` docs say you shouldn't override it][3]! > ## Notes to Inheritors > In derived classes, do not override the [`Close()`][4] method, > instead put all of the Stream cleanup logic in the > [`Dispose(Boolean)`][5] method. For more information, see > [Implementing a Dispose Method][1]. So we have a theoretical concern that `InputStreamInvoker.Close()` might not be idempotent, and that *might* be responsible for an `ObjectDisposedException`. Maybe. (At least it's a start?) Create the obvious idempotent unit test, let's see if it fails: var javaInputStream = new Java.IO.ByteArrayInputStream (new byte[]{0x1, 0x2, 0x3, 0x4}); var invoker = new InputStreamInvoker (javaInputStream); invoker.Dispose (); invoker.Dispose (); Calling `invoker.Dispose()` twice does not fail. It *is* idempotent, at least for this test data. However, with a slight change to that logic, we're not only able to make things break, but the breakage looks rather similar to the original `ObjectDisposedException`! var javaInputStream = new Java.IO.ByteArrayInputStream (new byte[]{0x1, 0x2, 0x3, 0x4}); var invoker = new InputStreamInvoker (javaInputStream); javaInputStream.Dispose (); invoker.Dispose (); fails with: at Java.Interop.JniPeerMembers.AssertSelf(IJavaPeerable self) at Java.Interop.JniPeerMembers.JniInstanceMethods.InvokeVirtualVoidMethod(String encodedMember, IJavaPeerable self, JniArgumentValue* parameters) at Java.IO.InputStream.Close() at Android.Runtime.InputStreamInvoker.Close() at System.IO.Stream.Dispose() Thus, a new hypothesis for #9039: we *somehow* have a `Java.IO.InputStream` instance being shared, and that shared instance is being `.Dispose()`d of from multiple places. Neither `InputStreamInvoker.Close()` nor `InputStreamInvoker.Dispose()` anticipated this, both invoke `BaseInputStream.Close()` on a now disposed instance, and the `ObjectDisposedException` is the result. TODO: try to validate this hypothesis. Perhaps it's related to the use of `BufferedStream` in `AndroidMessageHandler.GetContent()`? For now, follow the advice of the `Stream.Close()` docs, and "remove" the `InputStreamInvoker.Close()` and `OutputStreamInvoker.Close()` method overrides. Furthermore, update their `Dispose(bool)` methods to verify that `BaseInputStream` and/or `BaseOutputStream` are not disposed before invoking `.Close()`. (Note: the `Close()` methods aren't actually removed, because doing so makes the public API analyzers complain. Instead, `Close()` is retained, but it just calls `base.Close()`.) [0]: 34.0.95...34.0.113 [1]: https://learn.microsoft.com/dotnet/standard/garbage-collection/implementing-dispose [2]: https://github.com/dotnet/runtime/blob/2ea6ae57874c452923af059cbcb57d109564353c/src/libraries/System.Private.CoreLib/src/System/IO/Stream.cs#L156 [3]: https://learn.microsoft.com/dotnet/api/system.io.stream.close?view=net-8.0#notes-to-inheritors [4]: https://learn.microsoft.com/dotnet/api/system.io.stream.close?view=net-8.0#system-io-stream-close [5]: https://learn.microsoft.com/dotnet/api/system.io.stream.dispose?view=net-8.0#system-io-stream-dispose(system-boolean)
I also started having this error sporadically after updating MAUI dotnet workload list
{System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'Java.IO.InputStreamInvoker'.
at Java.Interop.JniPeerMembers.AssertSelf(IJavaPeerable self) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.cs:line 153
at Java.Interop.JniPeerMembers.JniInstanceMethods.InvokeVirtualVoidMethod(String encodedMember, IJavaPeerable self, JniArgumentValue* parameters) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods_Invoke.cs:line 57
at Java.IO.InputStream.Close() in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/obj/Release/net8.0/android-34/mcw/Java.IO.InputStream.cs:line 116
at Android.Runtime.InputStreamInvoker.Close() in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/InputStreamInvoker.cs:line 62
at System.IO.Stream.Dispose()
at System.IO.BufferedStream.Dispose(Boolean disposing)
at System.IO.Stream.Close()
at System.IO.Stream.Dispose()
at System.IO.DelegatingStream.Dispose(Boolean disposing)
at System.IO.Stream.Close()
at System.IO.StreamReader.Dispose(Boolean disposing)
at System.IO.StreamReader.Close()
at Newtonsoft.Json.JsonTextReader.Close()
at Newtonsoft.Json.JsonReader.Dispose(Boolean disposing)
at Newtonsoft.Json.JsonReader.System.IDisposable.Dispose()
at Extensions.SerializeExtensions.FromJson[IList`1](Stream Data)
at Client.Clients.<_processResponse>d__81`1[[System.Collections.Generic.IList`1[[Seller, Client, Version=1.2403.1.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext() in D:\Mobile\ServiceManager\deps\Client\src\Clients.cs:line 370
at Client.Sellers.Find(String Company, SellerFind Terms) in D:\Mobile\ServiceManager\deps\Client\src\Sellers.cs:line 34}
base: {System.InvalidOperationException}
Message: "Cannot access a disposed object.\nObject name: 'Java.IO.InputStreamInvoker'."
ObjectName: "Java.IO.InputStreamInvoker" Confirmed with this work loads works nice
|
Context: #9039 Context: #9039 (comment) Context: 0315e89 In #9039, a customer reports that starting with the .NET for Android workload 34.0.113, their app would start crashing with an `ObjectDisposedException`: ObjectDisposed_ObjectName_Name, Java.IO.InputStreamInvoker at Java.Interop.JniPeerMembers.AssertSelf(IJavaPeerable ) at Java.Interop.JniPeerMembers.JniInstanceMethods.InvokeVirtualVoidMethod(String , IJavaPeerable , JniArgumentValue* ) at Java.IO.InputStream.Close() at Android.Runtime.InputStreamInvoker.Close() at System.IO.Stream.Dispose() at System.IO.BufferedStream.Dispose(Boolean ) at System.IO.Stream.Close() at System.IO.Stream.Dispose() at System.Net.Http.StreamContent.Dispose(Boolean ) at System.Net.Http.HttpContent.Dispose() at System.Net.Http.HttpResponseMessage.Dispose(Boolean ) at Xamarin.Android.Net.AndroidHttpResponseMessage.Dispose(Boolean ) at System.Net.Http.HttpResponseMessage.Dispose() at System.Net.Http.HttpClient.HandleFailure(Exception , Boolean , HttpResponseMessage , CancellationTokenSource , CancellationToken , CancellationTokenSource ) at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage , HttpCompletionOption , CancellationTokenSource , Boolean , CancellationTokenSource , CancellationToken ) This was rather confusing, as between [34.0.95 and 34.0.113][0] the only change to `Mono.Android.dll` was 0315e89, which has nothing to do with networking. Additional consideration presented a hypothetical: [`IDisposable.Dispose()` should be idempotent][1]: > To help ensure that resources are always cleaned up appropriately, > a Dispose method should be idempotent, such that it's callable > multiple times without throwing an exception. *Is* `InputStreamInvoker.Dispose()` idempotent? An additional conundrum is that `InputStreamInvoker.Dispose()` doesn't exist; it's `Stream.Dispose()` that exists, and [`Stream.Dispose()` invokes `Stream.Close()`][2]. This in turn means that `Stream.Close()` must be idempotent, which in turn means that `InputStreamInvoker.Close()` must be idempotent. Additionally, [`Stream.Close()` docs say you shouldn't override it][3]! > ## Notes to Inheritors > In derived classes, do not override the [`Close()`][4] method, > instead put all of the Stream cleanup logic in the > [`Dispose(Boolean)`][5] method. For more information, see > [Implementing a Dispose Method][1]. So we have a theoretical concern that `InputStreamInvoker.Close()` might not be idempotent, and that *might* be responsible for an `ObjectDisposedException`. Maybe. (At least it's a start?) Create the obvious idempotent unit test, let's see if it fails: var javaInputStream = new Java.IO.ByteArrayInputStream (new byte[]{0x1, 0x2, 0x3, 0x4}); var invoker = new InputStreamInvoker (javaInputStream); invoker.Dispose (); invoker.Dispose (); Calling `invoker.Dispose()` twice does not fail. It *is* idempotent, at least for this test data. However, with a slight change to that logic, we're not only able to make things break, but the breakage looks rather similar to the original `ObjectDisposedException`! var javaInputStream = new Java.IO.ByteArrayInputStream (new byte[]{0x1, 0x2, 0x3, 0x4}); var invoker = new InputStreamInvoker (javaInputStream); javaInputStream.Dispose (); invoker.Dispose (); fails with: at Java.Interop.JniPeerMembers.AssertSelf(IJavaPeerable self) at Java.Interop.JniPeerMembers.JniInstanceMethods.InvokeVirtualVoidMethod(String encodedMember, IJavaPeerable self, JniArgumentValue* parameters) at Java.IO.InputStream.Close() at Android.Runtime.InputStreamInvoker.Close() at System.IO.Stream.Dispose() Thus, a new hypothesis for #9039: we *somehow* have a `Java.IO.InputStream` instance being shared, and that shared instance is being `.Dispose()`d of from multiple places. Neither `InputStreamInvoker.Close()` nor `InputStreamInvoker.Dispose()` anticipated this, both invoke `BaseInputStream.Close()` on a now disposed instance, and the `ObjectDisposedException` is the result. TODO: try to validate this hypothesis. Perhaps it's related to the use of `BufferedStream` in `AndroidMessageHandler.GetContent()`? For now, follow the advice of the `Stream.Close()` docs, and "remove" the `InputStreamInvoker.Close()` and `OutputStreamInvoker.Close()` method overrides. Furthermore, update their `Dispose(bool)` methods to verify that `BaseInputStream` and/or `BaseOutputStream` are not disposed before invoking `.Close()`. (Note: the `Close()` methods aren't actually removed, because doing so makes the public API analyzers complain. Instead, `Close()` is retained, but it just calls `base.Close()`.) [0]: 34.0.95...34.0.113 [1]: https://learn.microsoft.com/dotnet/standard/garbage-collection/implementing-dispose [2]: https://github.com/dotnet/runtime/blob/2ea6ae57874c452923af059cbcb57d109564353c/src/libraries/System.Private.CoreLib/src/System/IO/Stream.cs#L156 [3]: https://learn.microsoft.com/dotnet/api/system.io.stream.close?view=net-8.0#notes-to-inheritors [4]: https://learn.microsoft.com/dotnet/api/system.io.stream.close?view=net-8.0#system-io-stream-close [5]: https://learn.microsoft.com/dotnet/api/system.io.stream.dispose?view=net-8.0#system-io-stream-dispose(system-boolean)
I am also running into this exception:
Only happens on Android. The same code runs without problems on windows. |
I am back from vacation now, looks like theres effort in resolving this. |
I am also encountering this problem.
Installed workloads:
In my case it doesn't occur with normal synchronous Http calls (at least that's my assessment). I have a place where I create 3 tasks that I execute with a Task.WhenAll(...), and it's when these calls get made concurrently that the crash happens. Adding suggested Can anyone estimate the impact of not using the native Http handler? I really don't like hacks like this. |
I also face this issue... This is for Xamarin, but it makes me a bit worried to switch to the managed handler. |
@danielheddelin the only side-effect you might observe is a slight increase in the app bundle size of around a few hundred kB. You might also see different behavior when it comes to the default values of some headers ( @Brosten this does not apply to .NET anymore. We don't use BoringSSL anymore, both the managed handler and native handler in .NET 6+ are built on top of Android/Java APIs. Is there something in particular that still makes you worried to switch to the managed handler? |
@simonrozsival: Thanks for your reply! |
Also running into this after migrating from XA to .NET for Android, will see if I can set up a repro later today or over the weekend. Stack trace:
|
@Digifais what version of the .NET Android SDK are you using? Do you get this exception with the latest 34.0.138? |
@simonrozsival I was on 34.0.113, I've updated to 34.0.138 now and the exception still occurs. |
We had the same issue and ended up implementing the http client as a singelton. It solved the issue for us. |
I'm running into this issue as well and desperately looking for a solution or workaround. Implementing the http client as a singleton seems to work, but I want to avoid rewriting my implementation as much as possible. Also can't simply replace the native AndroidMessageHandler since our certificate pinning implementation relies on it. Wondering whether |
@zwikk would you able to share a repro project?
How do you implement certificate pinning? Are you using |
I don't have a repro project and also can't reproduce myself in our app. Exception logging reveal that our customers do experience the ObjectDisposedException in our app in production. The exception seems to occur exclusively with a single HTTP POST where we ignore the response. Cert pinning: we're implementing public class MyAndroidMessageHandler : AndroidMessageHandler
{
protected override IHostnameVerifier GetSSLHostnameVerifier(HttpsURLConnection connection)
{
return new CertificatePinningHostnameVerifier();
}
} We're using Refit, Polly and HttpClientFactory, a bunch of custom MessageHandlers, including the custom AndroidMessageHandler for cert pinning purposes. |
I'm still getting it randomly, but very often on my .NET MAUI app. This is how my .MAUI app .csproj looks like This is my workload list
And this is the stacktrace when the error occurs.
@jonathanpeppers any news or hint about how to workaround it? |
@ederbond you aren't using the latest Android release: You can install the VS 17.12 Preview, and just target .NET 8 instead of .NET 9 to try it. We were thinking this might improve things: |
@jonathanpeppers, it's still occurring for me on 34.0.138 |
@Digifais is the stack trace different? As you should not get a |
After updating to .138, the exception occurred again, but I think the stack is different
|
@jonathanpeppers The stack trace is identical as in my reply above on 34.0.138 |
If this works for someone, i found a workaround using Polly, i just added this I also use Polly with Refit. |
Android framework version
net8.0-android
Affected platform version
Android SDK 34.0.113
Description
Hello, and thank you for this amazing project.
We have a mobile application running MAUI in hospitals in Norway, where Android is one of the most used platforms. We needed to deliver a new version this week. Last Wednesday / Thursday we noticed that we started getting sporadic http client disposal messages. After some time we figured out that the only changes to the app was the Android SDK version delivered by .NET . 34.0.113 was released, and we noticed that pinning to 34.0.95 fixed the issues.
After a lot of times, I've actually not been able to give you a reproducible project , and due to internal policies in my company I am unable to share my project.
But I can give you a brief explanation of the architecture surrounding http client in the project:
The times I often see it is when I navigate between two pages that is using the same http client, but is using it against different request urls.
Here is the exception we keep seeing:
I understand that this a long shot as I do not have a reproducible project, but I hope that anyone in the team / community can reach out and see if they might have broken something, or have an idea on what we are potentially doing wrong.
Appreciate your time!
Steps to Reproduce
None unfortunately.
Did you find any workaround?
For now, we pin the SDK version by using the rollback feature of
dotnet workload install
, which saved our delivery, but we plan to upgrade every bits and pieces soon, so this will soon be an issue for us again.Relevant log output
No response
The text was updated successfully, but these errors were encountered: