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

Xamarin Android应用旋转屏幕时WebService调用崩溃的解决方案咨询

解决Xamarin Android屏幕旋转时WebService调用崩溃的问题

嘿,这个问题我太熟了——屏幕旋转时Android会销毁并重建Activity,你的异步WebService调用如果绑定在旧的Activity实例上,肯定会因为上下文丢失或者回调失效导致崩溃,甚至请求直接失败。下面给你几个经过实践验证的解决方案,按推荐程度排序:

方案1:使用ViewModel(最推荐,符合Android架构规范)

ViewModel是AndroidX Lifecycle库提供的组件,它的核心特性就是屏幕旋转时不会被销毁,完美适配这种跨配置变更的异步任务场景。你可以把WebService的调用逻辑放在ViewModel里,Activity只负责观察任务结果并更新UI,完全不用担心旋转导致的上下文失效。

代码示例:

首先创建你的ViewModel类:

using AndroidX.Lifecycle;
using System.Threading.Tasks;
using System;

public class WebServiceViewModel : ViewModel
{
    private bool _isTaskRunning;
    public bool IsTaskRunning => _isTaskRunning;

    // 定义回调事件,通知Activity结果
    public event Action<string> OnCallSuccess;
    public event Action<Exception> OnCallFailed;

    public async Task ExecuteWebServiceCall()
    {
        if (_isTaskRunning) return; // 避免重复调用
        _isTaskRunning = true;

        try
        {
            // 替换成你的实际WebService调用逻辑
            var response = await YourWebServiceClient.FetchDataAsync();
            OnCallSuccess?.Invoke(response);
        }
        catch (Exception ex)
        {
            OnCallFailed?.Invoke(ex);
        }
        finally
        {
            _isTaskRunning = false;
        }
    }
}

然后在Activity中使用ViewModel:

protected override void OnCreate(Bundle savedInstanceState)
{
    base.OnCreate(savedInstanceState);
    SetContentView(Resource.Layout.your_activity_layout);

    // 获取ViewModel实例,旋转时会复用同一个实例
    var viewModel = new ViewModelProvider(this).Get<WebServiceViewModel>();

    // 绑定结果回调(每次OnCreate都要绑定,因为Activity是新实例)
    viewModel.OnCallSuccess += result =>
    {
        Toast.MakeText(this, $"请求成功:{result}", ToastLength.Short).Show();
        // 这里可以放心更新UI,当前Activity是有效的
    };

    viewModel.OnCallFailed += ex =>
    {
        Toast.MakeText(this, $"请求失败:{ex.Message}", ToastLength.Short).Show();
    };

    // 仅在首次创建Activity时启动调用(避免旋转后重复触发)
    if (savedInstanceState == null)
    {
        _ = viewModel.ExecuteWebServiceCall();
    }
}

方案2:禁止Activity在屏幕旋转时重建

如果你的应用不需要横竖屏切换布局,或者可以自己处理布局变更,最简单的方式就是让系统不要在旋转时重建Activity。只需在Activity的属性中添加ConfigChanges配置即可。

代码示例:

[Activity(
    Label = "@string/app_name",
    MainLauncher = true,
    // 声明监听屏幕方向和尺寸变化,避免Activity重建
    ConfigChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.SmallestScreenSize
)]
public class MainActivity : AppCompatActivity
{
    private bool _isWebServiceInProgress;

    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        SetContentView(Resource.Layout.your_activity_layout);

        if (!_isWebServiceInProgress)
        {
            _isWebServiceInProgress = true;
            _ = CallWebServiceAsync();
        }
    }

    private async Task CallWebServiceAsync()
    {
        try
        {
            var result = await YourWebServiceClient.FetchDataAsync();
            Toast.MakeText(this, $"请求成功:{result}", ToastLength.Short).Show();
        }
        catch (Exception ex)
        {
            Toast.MakeText(this, $"请求失败:{ex.Message}", ToastLength.Short).Show();
        }
        finally
        {
            _isWebServiceInProgress = false;
        }
    }

    // 可选:如果需要处理横竖屏布局切换,重写这个方法
    public override void OnConfigurationChanged(Configuration newConfig)
    {
        base.OnConfigurationChanged(newConfig);
        // 根据newConfig.Orientation切换布局
        // 比如:if (newConfig.Orientation == Orientation.Landscape) { SetContentView(...); }
    }
}

这个方案的优点是实现简单,缺点是失去了系统自动适配横竖屏布局的能力,如果需要适配不同方向的布局,你得自己手动处理。

方案3:使用保留实例的Fragment(传统方案)

这是ViewModel出现之前常用的方案:创建一个无UI的Fragment,设置RetainInstance = true,这样屏幕旋转时Fragment不会被销毁,WebService调用放在Fragment中,Activity通过Fragment获取任务状态和结果。

代码示例:

首先创建保留Fragment:

public class WebServiceRetainedFragment : Fragment
{
    public event Action<string> OnCallSuccess;
    public event Action<Exception> OnCallFailed;
    private bool _isTaskRunning;

    public override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        RetainInstance = true; // 关键:设置保留实例
    }

    public async Task StartWebServiceCall()
    {
        if (_isTaskRunning) return;
        _isTaskRunning = true;

        try
        {
            var result = await YourWebServiceClient.FetchDataAsync();
            OnCallSuccess?.Invoke(result);
        }
        catch (Exception ex)
        {
            OnCallFailed?.Invoke(ex);
        }
        finally
        {
            _isTaskRunning = false;
        }
    }
}

然后在Activity中使用:

protected override void OnCreate(Bundle savedInstanceState)
{
    base.OnCreate(savedInstanceState);
    SetContentView(Resource.Layout.your_activity_layout);

    WebServiceRetainedFragment fragment;

    if (savedInstanceState == null)
    {
        // 首次创建时添加Fragment
        fragment = new WebServiceRetainedFragment();
        SupportFragmentManager.BeginTransaction()
            .Add(fragment, "WebServiceFragmentTag")
            .Commit();
        // 启动WebService调用
        _ = fragment.StartWebServiceCall();
    }
    else
    {
        // 旋转后获取已存在的Fragment实例
        fragment = SupportFragmentManager.FindFragmentByTag("WebServiceFragmentTag") as WebServiceRetainedFragment;
    }

    // 绑定回调
    fragment.OnCallSuccess += result =>
    {
        Toast.MakeText(this, $"请求成功:{result}", ToastLength.Short).Show();
    };

    fragment.OnCallFailed += ex =>
    {
        Toast.MakeText(this, $"请求失败:{ex.Message}", ToastLength.Short).Show();
    };
}

为什么你的原有方式会崩溃?

原来的WebService调用直接绑定在Activity中,当屏幕旋转时,旧的Activity被销毁,但异步任务还在执行,任务完成后回调引用的是旧的Activity实例,这会导致空引用异常或者上下文失效,最终引发崩溃。上面的方案都是通过将异步任务与Activity的生命周期解耦,避免了这个问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 08:10:55