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

.NET Maui Android平台USB串口读写权限及代码崩溃问题求助

.NET Maui USB串口设备权限修复与读取方案

一、解决PendingIntent崩溃问题

你的代码崩溃核心原因是Android 12+(API 31及以上)要求创建PendingIntent必须指定FLAG_IMMUTABLE或FLAG_MUTABLE,旧版的0值flag已被废弃。修改PendingIntent创建代码:

string ACTION_USB_PERMISSION = "com.MauiAndroidSerialPort.USB_PERMISSION";
PendingIntent mPermissionIntent;

if (Build.VERSION.SdkInt >= BuildVersionCodes.S)
{
    mPermissionIntent = PendingIntent.GetBroadcast(Platform.CurrentActivity, 0, new Intent(ACTION_USB_PERMISSION), PendingIntentFlags.Immutable);
}
else
{
    mPermissionIntent = PendingIntent.GetBroadcast(Platform.CurrentActivity, 0, new Intent(ACTION_USB_PERMISSION), 0);
}

二、正确的USB权限处理流程

权限请求是异步操作,不能直接在请求后立即调用OpenDevice,必须通过广播接收器接收权限结果:

  1. 定义全局变量
private BroadcastReceiver _usbPermissionReceiver;
private UsbManager _usbManager;
private UsbDevice _targetDevice;
private UsbInterface _serialInterface;
private UsbEndpoint _readEndpoint;
private UsbDeviceConnection _deviceConnection;
  1. 页面初始化时注册广播接收器
_usbManager = (UsbManager)Platform.CurrentActivity.GetSystemService(Context.UsbService);
string ACTION_USB_PERMISSION = "com.MauiAndroidSerialPort.USB_PERMISSION";

_usbPermissionReceiver = new BroadcastReceiver()
{
    OnReceive = (context, intent) =>
    {
        if (ACTION_USB_PERMISSION.Equals(intent.Action))
        {
            lock (this)
            {
                UsbDevice device = (UsbDevice)intent.GetParcelableExtra(UsbManager.ExtraDevice);
                if (intent.GetBooleanExtra(UsbManager.ExtraPermissionGranted, false))
                {
                    device?.InitializeSerialConnection();
                }
                else
                {
                    Console.WriteLine("USB权限被拒绝");
                }
            }
        }
    }
};

// 注册广播过滤器
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
Platform.CurrentActivity.RegisterReceiver(_usbPermissionReceiver, filter);
  1. 发起权限请求
// 假设已找到目标设备_targetDevice
if (!_usbManager.HasPermission(_targetDevice))
{
    PendingIntent permissionIntent;
    if (Build.VERSION.SdkInt >= BuildVersionCodes.S)
    {
        permissionIntent = PendingIntent.GetBroadcast(Platform.CurrentActivity, 0, new Intent(ACTION_USB_PERMISSION), PendingIntentFlags.Immutable);
    }
    else
    {
        permissionIntent = PendingIntent.GetBroadcast(Platform.CurrentActivity, 0, new Intent(ACTION_USB_PERMISSION), 0);
    }
    _usbManager.RequestPermission(_targetDevice, permissionIntent);
}
else
{
    _targetDevice.InitializeSerialConnection();
}

三、正确识别串口接口与端点

不要硬编码GetInterface(1),遍历设备接口找到符合串口特征的接口(通常是CDC数据类接口,端点为BULK类型):

private void InitializeSerialConnection(UsbDevice device)
{
    // 遍历设备接口,找到串口接口
    for (int i = 0; i < device.InterfaceCount; i++)
    {
        UsbInterface usbInterface = device.GetInterface(i);
        // 筛选CDC数据类接口(可根据设备实际类型调整)
        if (usbInterface.InterfaceClass == (int)UsbClass.CdcData)
        {
            _serialInterface = usbInterface;
            // 遍历端点,找到读取端点(IN方向)
            for (int j = 0; j < usbInterface.EndpointCount; j++)
            {
                UsbEndpoint endpoint = usbInterface.GetEndpoint(j);
                if (endpoint.Type == UsbAddressing.Bulk && endpoint.Direction == UsbDirection.In)
                {
                    _readEndpoint = endpoint;
                    break;
                }
            }
            break;
        }
    }

    if (_serialInterface == null || _readEndpoint == null)
    {
        Console.WriteLine("未找到有效的串口接口或读取端点");
        return;
    }

    // 打开设备连接并声明接口
    _deviceConnection = _usbManager.OpenDevice(device);
    if (_deviceConnection != null)
    {
        bool claimed = _deviceConnection.ClaimInterface(_serialInterface, true);
        if (claimed)
        {
            Console.WriteLine("接口声明成功,开始读取数据");
            // 启动读取线程
            Task.Run(ReadSerialData);
        }
        else
        {
            Console.WriteLine("接口声明失败");
            _deviceConnection.Close();
        }
    }
    else
    {
        Console.WriteLine("无法打开设备连接");
    }
}

四、串口数据读取实现

通过后台线程循环读取数据,避免阻塞UI线程:

private void ReadSerialData()
{
    byte[] buffer = new byte[1024]; // 根据设备调整缓冲区大小
    while (_deviceConnection != null && _serialInterface != null)
    {
        int bytesRead = _deviceConnection.BulkTransfer(_readEndpoint, buffer, buffer.Length, 5000);
        if (bytesRead > 0)
        {
            // 转换为字符串(根据设备编码调整,通常为UTF-8或ASCII)
            string data = Encoding.UTF8.GetString(buffer, 0, bytesRead).Trim();
            Console.WriteLine($"读取到数据:{data}");
            
            // 回到UI线程更新界面
            MainThread.BeginInvokeOnMainThread(() =>
            {
                // 在这里更新UI,例如绑定到Label控件
                // BarcodeLabel.Text = data;
            });
        }
        else if (bytesRead < 0)
        {
            Console.WriteLine("读取出错,断开连接");
            break;
        }
    }

    // 清理资源
    if (_deviceConnection != null)
    {
        _deviceConnection.ReleaseInterface(_serialInterface);
        _deviceConnection.Close();
        _deviceConnection = null;
    }
}

五、页面销毁时清理资源

在页面的OnDisappearing方法中注销广播接收器并释放资源:

protected override void OnDisappearing()
{
    base.OnDisappearing();
    if (_usbPermissionReceiver != null)
    {
        Platform.CurrentActivity.UnregisterReceiver(_usbPermissionReceiver);
        _usbPermissionReceiver = null;
    }
    if (_deviceConnection != null)
    {
        _deviceConnection.ReleaseInterface(_serialInterface);
        _deviceConnection.Close();
        _deviceConnection = null;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 00:32:45