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

使用HttpClient时Finally代码块未生效的问题排查

问题分析与解决方案

这个问题我之前做Xamarin.Forms项目时也碰到过,核心是异步操作的UI上下文同步和构造函数调用异步方法的限制在搞鬼,咱们一步步拆解解决:

核心问题根源

  1. 构造函数无法正确处理异步操作:构造函数不能标记为async,你用Task.Run包装调用的话,会让异步代码脱离UI上下文执行——哪怕finally块里确实把isFetchingData设为了false,但这个操作是在后台线程完成的,Xamarin.Forms的UI控件只能在UI线程接收更新通知,所以界面完全没反应。
  2. 字段未实现绑定属性:你代码里的isFetchingData看起来是个私有字段,而非实现了INotifyPropertyChanged的绑定属性,就算在UI线程赋值,界面也收不到变化通知。
  3. 上下文丢失导致更新失效:如果异步操作时用了ConfigureAwait(false),或者在非UI线程触发PropertyChanged,界面控件也不会同步更新状态。

具体修复步骤

1. 把异步初始化从构造函数移到页面生命周期

不要在ViewModel的构造函数里调用异步方法,改用页面的OnAppearing事件(这个方法允许标记为async void,是少数安全使用async void的场景):

// 你的页面代码
public partial class PatientsPage : ContentPage
{
    public PatientsPage()
    {
        InitializeComponent();
        BindingContext = new YourPatientViewModel(); // 假设已完成ViewModel实例化
    }

    protected override async void OnAppearing()
    {
        base.OnAppearing();
        var viewModel = BindingContext as YourPatientViewModel;
        if (viewModel != null)
        {
            await viewModel.GetAllPatients();
        }
    }
}

2. 把字段改成可绑定属性

确保isFetchingData和空列表判断相关的属性都实现INotifyPropertyChanged(假设你的ViewModel继承了实现该接口的基类,比如自带SetProperty方法的基类):

public class YourPatientViewModel : BaseViewModel
{
    // 替换原来的isFetchingData字段为绑定属性
    private bool _isFetchingData;
    public bool IsFetchingData
    {
        get => _isFetchingData;
        set => SetProperty(ref _isFetchingData, value); // 自动触发PropertyChanged
    }

    private bool _isListEmpty;
    public bool IsListEmpty
    {
        get => _isListEmpty;
        set => SetProperty(ref _isListEmpty, value);
    }

    public ObservableRangeCollection<PatientViewModel> AllPatients { get; } = new();
    public ObservableRangeCollection<PatientViewModel> Patients { get; } = new();

    private readonly HttpClient _httpClient;
    private readonly string _baseUrl;
    private readonly JsonSerializerSettings _jsonSerializerSettings;

    // 构造函数只做初始化,不调用异步方法
    public YourPatientViewModel(HttpClient httpClient, string baseUrl, JsonSerializerSettings jsonSerializerSettings)
    {
        _httpClient = httpClient;
        _baseUrl = baseUrl;
        _jsonSerializerSettings = jsonSerializerSettings;
    }

    public async Task GetAllPatients()
    {
        try
        {
            IsFetchingData = true;
            var response = await _httpClient.GetStringAsync($"{_baseUrl}/patient?query=ma");
            var resultPatients = JsonConvert.DeserializeObject<ObservableRangeCollection<PatientViewModel>>(response, _jsonSerializerSettings);
            
            AllPatients.ReplaceRange(resultPatients);
            Patients.ReplaceRange(resultPatients);
        }
        catch (Exception e)
        {
            Console.WriteLine("*****ERROR kon API niet ophalen");
            Console.WriteLine(e.Message);
        }
        finally
        {
            CheckIfEmptyList();
            IsFetchingData = false;
        }
    }

    private void CheckIfEmptyList()
    {
        IsListEmpty = AllPatients.Count == 0;
    }
}

3. 避免不必要的ConfigureAwait(false)

在ViewModel的异步方法里,除非是纯后台计算逻辑,否则不要给await加ConfigureAwait(false)——这样能确保await之后的代码(包括finally块)回到UI上下文执行,界面就能正常接收更新通知了。

为什么之前硬编码JSON正常?

因为用testJson时,你是同步执行反序列化和集合更新的,全程在UI线程上下文,所以界面能实时响应属性变化;而用HttpClient.GetStringAsync时,异步操作脱离了UI上下文,导致后续更新无法同步到界面。

内容的提问来源于stack exchange,提问作者Marijke

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:30:59