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

Xamarin.Forms异步方法无法获取MainPage控件实例解决方案

问题原因

你代码出现无限重启的核心问题是:

  • 每次执行var mainpage = new Views.MainPage()都会创建一个全新的MainPage实例,而MainPage的构造函数中会直接调用bth.Start()启动蓝牙循环任务
  • 新启动的蓝牙循环运行到发送数据的逻辑时,又会再次new MainPage,再次触发新的Start调用,形成无限递归,导致任务反复重启
  • 你new出来的MainPage并不是当前屏幕上正在显示的页面实例,上面的JoystickControl没有完成加载渲染,根本拿不到真实的摇杆操作值
修复方案

不要在蓝牙逻辑类中主动创建MainPage实例,直接复用当前已经显示的MainPage实例即可,推荐用传参的方式实现:

  • 首先修改Bth类,持有当前MainPage的引用,删除所有new MainPage的逻辑
class Bth : IBth
{
    // 存储当前真实显示的MainPage实例
    private MainPage _mainPage;
    private CancellationTokenSource _ct { get; set; }
    const int RequestResolveError = 1000;

    // 修改Start方法,接收MainPage实例参数
    public void Start(MainPage mainPage, string name, int sleepTime = 200, bool readAsCharArray = false)
    {
        _mainPage = mainPage;
        Task.Run(async () => loop(name, sleepTime, readAsCharArray));
    }

    private async Task loop(string name, int sleepTime, bool readAsCharArray)
    {
        BluetoothDevice device = null;
        BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter;
        BluetoothSocket BthSocket = null;

        _ct = new CancellationTokenSource();
        while (_ct.IsCancellationRequested == false)
        {
            try
            {
                await Task.Delay(sleepTime); // 替换Thread.Sleep,避免阻塞线程池
                adapter = BluetoothAdapter.DefaultAdapter;
                // 原有蓝牙扫描、连接逻辑保持不变
                // ...
                if (BthSocket.IsConnected)
                {
                    // 原有连接成功提示逻辑保持不变
                    // ...
                    while (_ct.IsCancellationRequested == false)
                    {
                        string joystickData = "";
                        // 访问UI控件必须切到主线程
                        MainThread.BeginInvokeOnMainThread(() =>
                        {
                            // 这里替换成你JoystickControl实际暴露值的属性,比如HorizontalValue、VerticalValue
                            joystickData = $"{_mainPage.JoystickControlElement.X},{_mainPage.JoystickControlElement.Y}";
                        });
                        var bytes = Encoding.ASCII.GetBytes($"{{{joystickData}}}-");
                        await BthSocket.OutputStream.WriteAsync(bytes, 0, bytes.Length);
                        await Task.Delay(100);
                    }
                }
            }
            catch(Exception ex)
            {
                MainThread.BeginInvokeOnMainThread(() =>
                {
                    service.toast.toastSuccess(ex.Message);
                });
            }
            finally
            {
                if (BthSocket != null)
                    BthSocket.Close();
                device = null;
                adapter = null;
            }
        }
    }

    public async Task<string> GetData()
    {
        string data = "";
        MainThread.BeginInvokeOnMainThread(() =>
        {
            data = $"{_mainPage.JoystickControlElement.X},{_mainPage.JoystickControlElement.Y}";
        });
        return data;
    }

    // 原有Cancel、PairedDevices方法保持不变
    // ...
}
  • 然后修改MainPage构造函数中调用Start的逻辑,把当前页面实例传进去
public partial class MainPage : ContentPage
{
    public static Bluetooth.IBth bth = new Bluetooth.Bth();
    public MainPage()
    {
        InitializeComponent();
        // 传入当前页面实例this,不要新创建
        Task.Run(async () => bth.Start(this, "mecanumWheelRobot", 200, false));
    }

    public CustomControls.JoystickControl JoystickControlElement
    {
        get { return JoystickControl; }
    }
    public CustomControls.JoystickControl JoystickControlElement1
    {
        get { return JoystickControl1; }
    }
}
其他注意事项
  • 所有UI控件的属性读取、方法调用必须在主线程执行,不能直接在后台Task中访问JoystickControl,否则会触发跨线程访问UI的异常
  • 后台异步循环中不要用Thread.Sleep(),会阻塞线程池线程,优先用await Task.Delay()实现等待
  • 不要在业务逻辑层随意创建Page实例,尤其是Page构造函数中包含启动逻辑的场景,很容易出现无限递归、资源泄漏问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 21:36:42