Xamarin重启HttpClientFactory引发对象已释放异常,如何正确初始化?
问题
我的Xamarin应用在Android和iOS启动时均抛出异常:"Cannot access a disposed object. Object name: 'MobileAuthenticatedStream'",该异常似乎在后台执行GET操作后出现。我当前的HttpClientFactory初始化代码如下,在应用启动和恢复时会重新运行Init方法,请问这种重新初始化方式是否正确?若不正确,该如何实现该功能?
当前初始化代码
static void Init() { var host = new HostBuilder() .ConfigureHostConfiguration(c => { c.AddCommandLine(new string[] { $"ContentRoot={FileSystem.AppDataDirectory}" }); }) .ConfigureServices((c, x) => { ConfigureServices(c, x); }) .Build(); ServiceProvider = host.Services; } static void ConfigureServices(HostBuilderContext ctx, IServiceCollection services) { services.AddSingleton<Helpers.ClientService>(); services.AddHttpClient("webClient", client => { client.DefaultRequestHeaders.Authorization = GenerateAuthHeader(); }) .AddPolicyHandler(GetRetryPolicy()); }
获取客户端代码
public class ClientService { IHttpClientFactory _httpClientFactory; public ClientService(IHttpClientFactory factory) { _httpClientFactory = factory; } public HttpClient GetClient() { return _httpClientFactory.CreateClient(App.ClientName); } }
使用代码
Helpers.ClientService service = App.ServiceProvider.GetService<Helpers.ClientService>(); HttpClient client = service.GetClient(); HttpResponseMessage response = await client.GetAsync(url);
解答
问题根源
当前的初始化方式不正确,核心问题有两点:
- 每次调用
Init都会创建全新的Host实例,旧的ServiceProvider、HttpClientFactory及单例ClientService会被丢弃,但后台未完成的HTTP请求可能仍在使用旧实例的资源,触发“访问已释放对象”的异常。 AddHttpClient配置阶段直接调用GenerateAuthHeader()设置授权头,这会导致授权头在服务注册时就固定下来,后续令牌过期或更新后,新创建的HttpClient仍会使用旧令牌,无法动态更新。
正确实现方案
1. 确保Host与ServiceProvider全局唯一
Host和ServiceProvider是应用级别的核心资源,应只初始化一次。修改Init方法,避免重复创建:
static void Init() { if (ServiceProvider != null) return; // 已初始化则直接返回 var host = new HostBuilder() .ConfigureHostConfiguration(c => { c.AddCommandLine(new string[] { $"ContentRoot={FileSystem.AppDataDirectory}" }); }) .ConfigureServices((c, x) => { ConfigureServices(c, x); }) .Build(); ServiceProvider = host.Services; }
2. 动态注入授权头
放弃在服务注册阶段固定授权头,改为每次请求时注入最新令牌,推荐两种实现方式:
方案A:修改ClientService动态更新授权头
public class ClientService { private readonly IHttpClientFactory _httpClientFactory; public ClientService(IHttpClientFactory factory) { _httpClientFactory = factory; } public HttpClient GetClient() { var client = _httpClientFactory.CreateClient(App.ClientName); // 每次获取客户端时注入最新授权头 client.DefaultRequestHeaders.Authorization = GenerateAuthHeader(); return client; } }
方案B:使用DelegatingHandler拦截请求(更优雅)
先创建自定义授权拦截器:
public class AuthHeaderHandler : DelegatingHandler { protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // 每次发起请求前注入最新授权头 request.Headers.Authorization = GenerateAuthHeader(); return await base.SendAsync(request, cancellationToken); } }
然后修改服务注册逻辑,添加拦截器:
static void ConfigureServices(HostBuilderContext ctx, IServiceCollection services) { services.AddSingleton<AuthHeaderHandler>(); services.AddSingleton<Helpers.ClientService>(); services.AddHttpClient("webClient") .AddPolicyHandler(GetRetryPolicy()) .AddHttpMessageHandler<AuthHeaderHandler>(); // 绑定授权拦截器 }
3. 应用恢复时的处理
应用恢复时无需重建Host,只需调用令牌刷新逻辑即可——后续请求会自动通过上述动态方案使用新令牌。同时建议给后台请求绑定应用生命周期的取消令牌,避免休眠期间的请求在恢复后继续占用已释放资源。
内容的提问来源于stack exchange,提问作者Finlay Brown
相关产品推荐
相关产品推荐

