MASA MAUI Plugin 安卓蓝牙低功耗(二)蓝牙通讯
项目背景
MAUI的出现,赋予了广大Net开发者开发多平台应用的能力,MAUI 是Xamarin.Forms演变而来,但是相比Xamarin性能更好,可扩展性更强,结构更简单。但是MAUI对于平台相关的实现并不完整。所以MASA团队开展了一个实验性项目,意在对微软MAUI的补充和扩展
项目地址
https://github.com/BlazorComponent/MASA.Blazor/tree/main/src/Masa.Blazor.Maui.Plugin
每个功能都有单独的demo演示项目,考虑到app安装文件体积(虽然MAUI已经集成裁剪功能,但是该功能对于代码本身有影响),届时每一个功能都会以单独的nuget包的形式提供,方便测试,现在项目才刚刚开始,但是相信很快就会有可以交付的内容啦。
前言
本系列文章面向移动开发小白,从零开始进行平台相关功能开发,演示如何参考平台的官方文档使用MAUI技术来开发相应功能。
介绍
上一篇文章我们实现了蓝牙BLE的扫描功能,这里我们继续实现通讯功能。
本文JAVA相关代码均来自安卓开发者官网
开发步骤
连接到 GATT 服务器
通用属性配置文件Generic Attribute Profile简称GATT。
GATT定义了属性类型并规定了如何使用,包括了一个数据传输和存储的框架和一些基本操作。中间包含了一些概念如特性characteristics,服务services等。同时还定义了发现服务,特性和服务间的连接的处理过程,也包括读写特性值。
我们使用移远的FC410举例
通过nRF connect工具可以查看设备的配置,该设备有一个前缀为FFFF的主服务,该服务下有一个前缀为FF01的特征,该特征具有通知Notify 和写入Write两种属性(如果有Notify,那么就会有描述符)。换句话说我们可以通过这个特征给设备发送数据,而且可以通过订阅该特征值变化事件,来获取设备通过蓝牙的返回信息。
与 BLE 设备交互的第一步便是连接到 GATT 服务器。更具体地说,是连接到设备上的 GATT 服务器。
我们先看一下JAVA的实现方式
JAVA代码
bluetoothGatt = device.connectGatt(this, false, gattCallback);
连接到 BLE 设备上的 GATT 服务器,需要使用 connectGatt() 方法。此方法采用三个参数:一个 Context 对象、autoConnect(布尔值,指示是否在可用时自动连接到 BLE 设备),以及对 BluetoothGattCallback 的引用。该方法 BluetoothGatt 实例,然后可使用该实例执行 GATT 客户端操作。调用方(Android 应用)是 GATT 客户端。BluetoothGattCallback 用于向客户端传递结果(例如连接状态),以及任何进一步的 GATT 客户端操作。
我们再看一下BluetoothGattCallback 的JAVA实现
JAVA 代码
// Various callback methods defined by the BLE API.
private final BluetoothGattCallback gattCallback =
new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {
intentAction = ACTION_GATT_CONNECTED;
connectionState = STATE_CONNECTED;
broadcastUpdate(intentAction);
Log.i(TAG, "Connected to GATT server.");
Log.i(TAG, "Attempting to start service discovery:" +
bluetoothGatt.discoverServices());
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
intentAction = ACTION_GATT_DISCONNECTED;
connectionState = STATE_DISCONNECTED;
Log.i(TAG, "Disconnected from GATT server.");
broadcastUpdate(intentAction);
}
}
@Override
// New services discovered
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
@Override
// Result of a characteristic read operation
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
...
因为日后还需要实现其他平台的功能,我们的想法是所有公共部分都放到项目根目录,平台相关的实现,放到对应Platforms目录下对应平台的文件夹内,然后通过分部类的方式组织类结构。平台相关的方法起名以Platform为前缀。
我们先在Masa.Blazor.Maui.Plugin.Bluetooth项目Platforms->Android目录新建一个名称为RemoteGattServer.android.cs的分部类,然后添加初始化方法和BluetoothGattCallback
partial class RemoteGattServer
{
private Android.Bluetooth.BluetoothGatt _gatt;
private Android.Bluetooth.BluetoothGattCallback _gattCallback;
private void PlatformInit()
{
_gattCallback = new GattCallback(this);
_gatt = ((Android.Bluetooth.BluetoothDevice)Device).ConnectGatt(Android.App.Application.Context, false, _gattCallback);
}
public static implicit operator Android.Bluetooth.BluetoothGatt(RemoteGattServer gatt)
{
return gatt._gatt;
}
internal event EventHandler<CharacteristicEventArgs> CharacteristicRead;
internal event EventHandler<GattEventArgs> ServicesDiscovered;
private bool _servicesDiscovered = false;
...
internal class GattCallback : Android.Bluetooth.BluetoothGattCallback
{
private readonly RemoteGattServer _remoteGattServer;
internal GattCallback(RemoteGattServer remoteGattServer)
{
_remoteGattServer = remoteGattServer;
}
...
public override void OnCharacteristicWrite(Android.Bluetooth.BluetoothGatt gatt, Android.Bluetooth.BluetoothGattCharacteristic characteristic, Android.Bluetooth.GattStatus status)
{
System.Diagnostics.Debug.WriteLine($"CharacteristicWrite {
characteristic.Uuid} Status:{
status}");
_remoteGattServer.CharacteristicWrite?.Invoke(_remoteGattServer, new CharacteristicEventArgs {
Characteristic = characteristic, Status = status });
}
}
}
...
internal class ConnectionStateEventArgs : GattEventArgs
{
public Android.Bluetooth.ProfileState State
{
get; internal set;