MAUI项目架构设计
平台特定实现
接口定义
public interface ISerialPortService
{
Task<string[]> GetAvailablePortsAsync();
Task<bool> ConnectAsync(string portName, int baudRate);
Task DisconnectAsync();
Task SendDataAsync(byte[] data);
Task SendTextAsync(string text);
event EventHandler<SerialDataEventArgs> DataReceived;
event EventHandler<bool> ConnectionChanged;
bool IsConnected { get; }
}
public class SerialDataEventArgs : EventArgs
{
public byte[] Data { get; set; }
public string Text { get; set; }
public DateTime Timestamp { get; set; }
}
Windows平台实现
#if WINDOWS
using System.IO.Ports;
public class WindowsSerialPortService : ISerialPortService
{
private SerialPort _serialPort;
private bool _isConnected;
public bool IsConnected => _isConnected;
public event EventHandler<SerialDataEventArgs> DataReceived;
public event EventHandler<bool> ConnectionChanged;
public WindowsSerialPortService()
{
_serialPort = new SerialPort();
_serialPort.DataReceived += OnDataReceived;
}
public async Task<string[]> GetAvailablePortsAsync()
{
return await Task.FromResult(SerialPort.GetPortNames());
}
public async Task<bool> ConnectAsync(string portName, int baudRate)
{
try
{
if (_isConnected)
await DisconnectAsync();
_serialPort.PortName = portName;
_serialPort.BaudRate = baudRate;
_serialPort.DataBits = 8;
_serialPort.Parity = Parity.None;
_serialPort.StopBits = StopBits.One;
_serialPort.Open();
_isConnected = true;
ConnectionChanged?.Invoke(this, true);
return true;
}
catch (Exception ex)
{
_isConnected = false;
ConnectionChanged?.Invoke(this, false);
return false;
}
}
public async Task DisconnectAsync()
{
try
{
if (_serialPort?.IsOpen == true)
{
_serialPort.Close();
}
_isConnected = false;
ConnectionChanged?.Invoke(this, false);
}
catch (Exception)
{
}
}
public async Task SendDataAsync(byte[] data)
{
if (!_isConnected || !_serialPort.IsOpen)
throw new InvalidOperationException("串口未连接");
await Task.Run(() => _serialPort.Write(data, 0, data.Length));
}
public async Task SendTextAsync(string text)
{
var data = System.Text.Encoding.UTF8.GetBytes(text);
await SendDataAsync(data);
}
private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
var buffer = new byte[_serialPort.BytesToRead];
var bytesRead = _serialPort.Read(buffer, 0, buffer.Length);
var eventArgs = new SerialDataEventArgs
{
Data = buffer.Take(bytesRead).ToArray(),
Text = System.Text.Encoding.UTF8.GetString(buffer, 0, bytesRead),
Timestamp = DateTime.Now
};
DataReceived?.Invoke(this, eventArgs);
}
catch (Exception)
{
}
}
}
#endif
Android平台实现
#if ANDROID
using Android.Hardware.Usb;
using AndroidX.Core.Content;
public class AndroidSerialPortService : ISerialPortService
{
private UsbManager _usbManager;
private UsbDevice _usbDevice;
private UsbDeviceConnection _connection;
private UsbInterface _interface;
private UsbEndpoint _endpointIn;
private UsbEndpoint _endpointOut;
private bool _isConnected;
private CancellationTokenSource _readCancellation;
public bool IsConnected => _isConnected;
public event EventHandler<SerialDataEventArgs> DataReceived;
public event EventHandler<bool> ConnectionChanged;
public AndroidSerialPortService()
{
var context = Platform.CurrentActivity ?? Android.App.Application.Context;
_usbManager = (UsbManager)context.GetSystemService(Android.Content.Context.UsbService);
}
public async Task<string[]> GetAvailablePortsAsync()
{
var deviceList = _usbManager.DeviceList;
var portNames = new List<string>();
foreach (var device in deviceList.Values)
{
if (IsSerialDevice(device))
{
portNames.Add($"USB-{device.DeviceName}");
}
}
return portNames.ToArray();
}
public async Task<bool> ConnectAsync(string portName, int baudRate)
{
try
{
var deviceList = _usbManager.DeviceList;
foreach (var device in deviceList.Values)
{
if ($"USB-{device.DeviceName}" == portName)
{
_usbDevice = device;
break;
}
}
if (_usbDevice == null)
return false;
if (!_usbManager.HasPermission(_usbDevice))
{
return false;
}
_connection = _usbManager.OpenDevice(_usbDevice);
if (_connection == null)
return false;
_interface = _usbDevice.GetInterface(0);
_connection.ClaimInterface(_interface, true);
for (int i = 0; i < _interface.EndpointCount; i++)
{
var endpoint = _interface.GetEndpoint(i);
if (endpoint.Direction == UsbAddressing.In)
_endpointIn = endpoint;
else if (endpoint.Direction == UsbAddressing.Out)
_endpointOut = endpoint;
}
_isConnected = true;
ConnectionChanged?.Invoke(this, true);
StartDataReading();
return true;
}
catch (Exception)
{
_isConnected = false;
ConnectionChanged?.Invoke(this, false);
return false;
}
}
public async Task DisconnectAsync()
{
_isConnected = false;
_readCancellation?.Cancel();
_connection?.ReleaseInterface(_interface);
_connection?.Close();
ConnectionChanged?.Invoke(this, false);
}
public async Task SendDataAsync(byte[] data)
{
if (!_isConnected || _connection == null || _endpointOut == null)
throw new InvalidOperationException("设备未连接");
await Task.Run(() =>
{
_connection.BulkTransfer(_endpointOut, data, data.Length, 1000);
});
}
public async Task SendTextAsync(string text)
{
var data = System.Text.Encoding.UTF8.GetBytes(text);
await SendDataAsync(data);
}
private void StartDataReading()
{
_readCancellation = new CancellationTokenSource();
Task.Run(async () =>
{
var buffer = new byte[1024];
while (!_readCancellation.Token.IsCancellationRequested && _isConnected)
{
try
{
var bytesRead = _connection.BulkTransfer(_endpointIn, buffer, buffer.Length, 100);
if (bytesRead > 0)
{
var eventArgs = new SerialDataEventArgs
{
Data = buffer.Take(bytesRead).ToArray(),
Text = System.Text.Encoding.UTF8.GetString(buffer, 0, bytesRead),
Timestamp = DateTime.Now
};
DataReceived?.Invoke(this, eventArgs);
}
await Task.Delay(10);
}
catch (Exception)
{
}
}
});
}
private bool IsSerialDevice(UsbDevice device)
{
return device.DeviceClass == UsbClass.CdcData ||
device.DeviceClass == UsbClass.Comm;
}
}
#endif
MAUI主界面实现
public partial class MainPage : ContentPage
{
private readonly ISerialPortService _serialService;
private readonly ObservableCollection<string> _receivedMessages;
public MainPage(ISerialPortService serialService)
{
InitializeComponent();
_serialService = serialService;
_receivedMessages = new ObservableCollection<string>();
MessagesCollectionView.ItemsSource = _receivedMessages;
_serialService.DataReceived += OnDataReceived;
_serialService.ConnectionChanged += OnConnectionChanged;
LoadAvailablePorts();
}
private async void LoadAvailablePorts()
{
try
{
var ports = await _serialService.GetAvailablePortsAsync();
PortPicker.ItemsSource = ports;
if (ports.Length > 0)
PortPicker.SelectedIndex = 0;
}
catch (Exception ex)
{
await DisplayAlert("错误", $"加载串口列表失败: {ex.Message}", "确定");
}
}
private async void OnConnectClicked(object sender, EventArgs e)
{
try
{
if (_serialService.IsConnected)
{
await _serialService.DisconnectAsync();
}
else
{
if (PortPicker.SelectedItem == null)
{
await DisplayAlert("提示", "请选择串口", "确定");
return;
}
var portName = PortPicker.SelectedItem.ToString();
var baudRate = int.Parse(BaudRatePicker.SelectedItem?.ToString() ?? "9600");
var success = await _serialService.ConnectAsync(portName, baudRate);
if (!success)
{
await DisplayAlert("错误", "连接失败", "确定");
}
}
}
catch (Exception ex)
{
await DisplayAlert("错误", $"连接操作失败: {ex.Message}", "确定");
}
}
private async void OnSendClicked(object sender, EventArgs e)
{
try
{
if (!_serialService.IsConnected)
{
await DisplayAlert("提示", "请先连接串口", "确定");
return;
}
var text = SendEntry.Text;
if (string.IsNullOrWhiteSpace(text))
{
await DisplayAlert("提示", "请输入要发送的内容", "确定");
return;
}
await _serialService.SendTextAsync(text + "\r\n");
SendEntry.Text = string.Empty;
_receivedMessages.Add($"[发送] {DateTime.Now:HH:mm:ss} - {text}");
}
catch (Exception ex)
{
await DisplayAlert("错误", $"发送失败: {ex.Message}", "确定");
}
}
private void OnDataReceived(object sender, SerialDataEventArgs e)
{
MainThread.BeginInvokeOnMainThread(() =>
{
_receivedMessages.Add($"[接收] {e.Timestamp:HH:mm:ss} - {e.Text.Trim()}");
if (_receivedMessages.Count > 0)
{
MessagesCollectionView.ScrollTo(_receivedMessages.Last());
}
});
}
private void OnConnectionChanged(object sender, bool isConnected)
{
MainThread.BeginInvokeOnMainThread(() =>
{
ConnectButton.Text = isConnected ? "断开" : "连接";
StatusLabel.Text = isConnected ? "已连接" : "未连接";
StatusLabel.TextColor = isConnected ? Colors.Green : Colors.Red;
PortPicker.IsEnabled = !isConnected;
BaudRatePicker.IsEnabled = !isConnected;
SendButton.IsEnabled = isConnected;
SendEntry.IsEnabled = isConnected;
});
}
private async void OnRefreshPortsClicked(object sender, EventArgs e)
{
await LoadAvailablePorts();
}
}
依赖注入配置
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
#if WINDOWS
builder.Services.AddSingleton<ISerialPortService, WindowsSerialPortService>();
#elif ANDROID
builder.Services.AddSingleton<ISerialPortService, AndroidSerialPortService>();
#elif IOS
builder.Services.AddSingleton<ISerialPortService, iOSSerialPortService>();
#elif MACCATALYST
builder.Services.AddSingleton<ISerialPortService, MacCatalystSerialPortService>();
#endif
builder.Services.AddTransient<MainPage>();
return builder.Build();
}
}
相关学习资源
.NET MAUI开发
移动端开发
平台特定实现
依赖注入与架构
移动应用发布
跨平台开发最佳实践
性能优化
测试与调试
开源项目参考
