You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

解答

问题根源

当前的初始化方式不正确,核心问题有两点:

  1. 每次调用Init都会创建全新的Host实例,旧的ServiceProvider、HttpClientFactory及单例ClientService会被丢弃,但后台未完成的HTTP请求可能仍在使用旧实例的资源,触发“访问已释放对象”的异常。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 17:45:31