共计 7166 个字符,预计需要花费 18 分钟才能阅读完成。
我的项目背景
MAUI 的呈现,赋予了宽广 Net 开发者开发多平台利用的能力,MAUI 是 Xamarin.Forms 演变而来,然而相比 Xamarin 性能更好,可扩展性更强,构造更简略。然而 MAUI 对于平台相干的实现并不残缺。所以 MASA 团队发展了一个实验性我的项目,意在对微软 MAUI 的补充和扩大,
我的项目地址:https://github.com/BlazorComp…
每个性能都有独自的 demo 演示我的项目,思考到 app 安装文件体积(尽管 MAUI 曾经集成裁剪性能,然而该性能对于代码自身有影响),届时每一个性能都会以独自的 nuget 包的模式提供,不便测试,当初我的项目才刚刚开始,然而置信很快就会有能够交付的内容啦。
前言
本系列文章面向挪动开发小白,从零开始进行平台相干性能开发,演示如何参考平台的官网文档应用 MAUI 技术来开发相应性能。
介绍
之前两篇文章咱们实现了安卓蓝牙 BLE 的相干性能,本文咱们将 IOS 的 BLE 性能实现一下。
,思考到 Swift 语法对于 c# 开发人员更敌对,本文示例代码参考 Swift,相干代码来自苹果开发者官网
https://developer.apple.com/d…
开发步骤
批改我的项目
在 Masa.Blazor.Maui.Plugin.Bluetooth 我的项目中的 Platforms->iOS 文件夹下,增加一个局部类 MasaMauiBluetoothService,在安卓中有BluetoothManager,在 ios 中对应的是CBCentralManager,然而不同有安卓还有个适配器 Adapter 的概念,在 ios 中对于设施扫描、连贯和治理外围设备的对象,都是通过CBCentralManager 间接治理的,咱们看一下他的初始化办法
init(
delegate: CBCentralManagerDelegate?,
queue: DispatchQueue?,
options: [String : Any]? = nil
)
delegate:接管核心事件的委托。相当于咱们在装置中实现的 DevicesCallback
queue:用于调度核心角色事件的调度队列。如果该值为 nil,则地方管理器将应用主队列分派核心角色事件。这个咱们能够简略的了解为和安卓的 UI 线程或者后盾线程对应,更详尽的阐明请参考
https://developer.apple.com/d…
options:配置信息,咱们这里只用到了 ShowPowerAlert,代表蓝牙设施如果不可用,给用户提示信息。就好比你用了不符合标准的数据线,iphone 会给你提醒是一个意思。
public static partial class MasaMauiBluetoothService
{private static BluetoothDelegate _delegate = new();
public static CBCentralManager _manager = new CBCentralManager(_delegate, DispatchQueue.DefaultGlobalQueue, new CBCentralInitOptions
{ShowPowerAlert = true,});
private sealed class BluetoothDelegate : CBCentralManagerDelegate
{private readonly EventWaitHandle _eventWaitHandle = new(false, EventResetMode.AutoReset);
public List<BluetoothDevice> Devices {get;} = new();
public void WaitOne()
{Task.Run(async () =>
{await Task.Delay(5000);
_eventWaitHandle.Set();});
_eventWaitHandle.WaitOne();}
public override void DiscoveredPeripheral(CBCentralManager central, CBPeripheral peripheral,
NSDictionary advertisementData,
NSNumber RSSI)
{System.Diagnostics.Debug.WriteLine("OnScanResult");
if (!Devices.Contains(peripheral))
{Devices.Add(peripheral);
}
}
[Preserve]
public override void UpdatedState(CBCentralManager central)
{}}
}
咱们将 MasaMauiBluetoothService 批改为动态类,
咱们自定义的 BluetoothDelegate 继承自 CBCentralManagerDelegate,篇幅问题咱们这里先只重写DiscoveredPeripheral 和 UpdatedState,咱们这次的演示不须要实现 UpdatedState,然而这里的重写必须先放上去,否则调试过程会呈现上面的报错
ObjCRuntime.ObjCException: ‘Objective-C exception thrown. Name: NSInvalidArgumentException Reason: -[Masa_Blazor_Maui_Plugin_Bluetooth_MasaMauiBluetoothService_BluetoothDelegate centralManagerDidUpdateState:]: unrecognized selector sent to instance 0x284bfe200
另外有一点须要特地留神,这个 UpdatedState 办法我没有实现的代码,那么我就须要增加一个 [Preserve],这样是为了避免 链接器 在生成 nuget 包的时候把这个办法帮我优化掉。
实现发现左近设施性能,_eventWaitHandle和安卓一样,我这里只是实现了一个异步转同步不便间接通过 Devices 拿到后果,如果小伙伴不喜爱前期我会增加不阻塞的形式。
这里之所以能够 Devices.Contains 和Devices.Add是因为咱们在 BluetoothDevice 类中实现了隐式转换
如下是 iOS 目录下 BluetoothDevice.ios.cs 的局部代码
partial class BluetoothDevice
{
...
private BluetoothDevice(CBPeripheral peripheral)
{_peripheral = peripheral;}
public static implicit operator BluetoothDevice(CBPeripheral peripheral)
{return peripheral == null ? null : new BluetoothDevice(peripheral);
}
public static implicit operator CBPeripheral(BluetoothDevice device)
{return device._peripheral;}
...
ios 扫描外围设备是通过 scanForPeripherals
咱们持续在 MasaMauiBluetoothService 增加一个扫描附件设施的办法,咱们看一下 Swift 的文档
func scanForPeripherals(withServices serviceUUIDs: [CBUUID]?,
options: [String : Any]? = nil
)
serviceUUIDs:代表须要过滤的服务 UUID,相似安卓的 scanFilter 对象。
option:提供扫描的选项,咱们这里用到了 AllowDuplicatesKey,该值指定扫描是否应在不反复筛选的状况下运行
咱们参照实现以下咱们的 PlatformScanForDevices 办法
private static async Task<IReadOnlyCollection<BluetoothDevice>> PlatformScanForDevices()
{if (!_manager.IsScanning)
{_manager.ScanForPeripherals(new CBUUID[] { }, new PeripheralScanningOptions
{AllowDuplicatesKey = true});
await Task.Run(() => { _delegate.WaitOne(); });
_manager.StopScan();
_discoveredDevices = _delegate.Devices.AsReadOnly();}
return _discoveredDevices;
}
通过 _cbCentralManager.IsScanning来判断是否处于扫描状态,如果没有,那就就通过 ScanForPeripherals 扫描外围设备,扫描 5 秒之后(BluetoothDelegate 外部管制)通过 StopScan 进行扫描,并通过 _discoveredDevices 保留后果。
咱们还需实现 PlatformIsEnabledIsEnabled 和PlatformCheckAndRequestBluetoothPermission办法,用来在扫描之前查看蓝牙是否可用并且曾经通过用户受权
public static bool PlatformIsEnabledIsEnabled()
{return _manager.State == CBManagerState.PoweredOn;}
public static async Task<PermissionStatus> PlatformCheckAndRequestBluetoothPermission()
{PermissionStatus status = await Permissions.CheckStatusAsync<BluetoothPermissions>();
if (status == PermissionStatus.Granted)
return status;
if (status == PermissionStatus.Denied && DeviceInfo.Platform == DevicePlatform.iOS)
{
// Prompt the user to turn on in settings
// On iOS once a permission has been denied it may not be requested again from the application
return status;
}
status = await Permissions.RequestAsync<BluetoothPermissions>();
return status;
}
private class BluetoothPermissions : Permissions.BasePlatformPermission
{
protected override Func<IEnumerable<string>> RequiredInfoPlistKeys
=>
() => new string[] {"NSBluetoothAlwaysUsageDescription", "NSBluetoothPeripheralUsageDescription"};
public override Task<PermissionStatus> CheckStatusAsync()
{EnsureDeclared();
return Task.FromResult(GetBleStatus());
}
private PermissionStatus GetBleStatus() //Todo:Needs to be replenished
{
var status = _cbCentralManager.State;
return status switch
{
CBManagerState.PoweredOn=> PermissionStatus.Granted,
CBManagerState.Unauthorized => PermissionStatus.Denied,
CBManagerState.Resetting => PermissionStatus.Restricted,
_ => PermissionStatus.Unknown,
};
}
}
在 PlatformIsEnabledIsEnabled 办法中通过 _cbCentralManager.State == CBManagerState.PoweredOn 来判断蓝牙是否可用。该状态一共有如下枚举,从字面意思很好了解
**Unknown**, // 手机没有辨认到蓝牙
**Resetting**, // 手机蓝牙已断开连接
**Unsupported**, // 手机蓝牙性能没有权限
**Unauthorized**, // 手机蓝牙性能没有权限
**PoweredOff**,// 手机蓝牙性能敞开
**PoweredOn** // 蓝牙开启且可用
权限查看这里和安卓有一些区别,在重写的 RequiredInfoPlistKeys 办法中指定了须要查看的蓝牙权限,BasePlatformPermission的 EnsureDeclared 办法用来查看是否在 Info.plist 文件增加了须要的权限,GetBleStatus办法通过 _cbCentralManager 的状态,来查看受权状况。
咱们在 Masa.Blazor.Maui.Plugin.Bluetooth 的根目录增加局部类 MasaMauiBluetoothService.cs,向使用者提供ScanForDevicesAsync 等办法,办法外部通过 PlatformScanForDevices 来调用具体平台的实现。
public static partial class MasaMauiBluetoothService
{
private static IReadOnlyCollection<BluetoothDevice> _discoveredDevices;
public static Task<IReadOnlyCollection<BluetoothDevice>> ScanForDevicesAsync()
{return PlatformScanForDevices();
}
public static bool IsEnabled()
{return PlatformIsEnabledIsEnabled();
}
public static async Task<PermissionStatus> CheckAndRequestBluetoothPermission()
{return await PlatformCheckAndRequestBluetoothPermission();
}
}
应用
右键 Masa.Blazor.Maui.Plugin.Bluetooth 我的项目,点击打包,生成一个 nuget 包,在 Masa.Blazor.Maui.Plugin.BlueToothSample 我的项目中离线装置即可,代码的应用与安卓齐全一样,只是权限配置形式不同
在Masa.Blazor.Maui.Plugin.BlueToothSample我的项目的 Platforms->iOS->Info.plist 中增加蓝牙相干权限
<key>NSBluetoothAlwaysUsageDescription</key>
<string>App required to access Bluetooth</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>App required to access Bluetooth</string>
NSBluetoothAlwaysUsageDescription对应 iOS 13 以上版本,对于 iOS 13 之前的版本,须要将 NSBluetoothAlwaysUsageDescription 和NSBluetoothPeripheralUsageDescription同时增加。
蓝牙扫描的成果和安卓机是齐全一样的,这里就不展现了。前文详情
iOS 调试及谬误排查
目前在 windows 的 vs 环境调试 MAUI 的 ios 程序,是不须要 mac 电脑反对的,数据线连上后会显示一个本地设施,然而你依然须要一个开发者账号,vs 会调用 apple 开发者 api 主动帮你配置好须要的证书。
1、如果没有显示查看 Xamarin->iOS 设置,热重启是否开启
2、调试过程如果提醒相似
Could not find executable for C:\Users\xxx\AppData\Local\Temp\hbjayi2h.ydn
找不到文件的状况,右键抉择清理我的项目即可,如果无奈解决手动删除 bin 和 obj 目录重试
3、调试过程如果 app 无端退出,排查一下思考 APP 的启动和调试断点工夫,iOS 要求所有办法必须在 17 秒之内返回,否则 iOS 零碎将进行该利用
4、调试过程呈现 Deploy Error: An Lockdown error occurred. The error code was “MuxError” 的谬误,请查看你的数据线,从新插拔或者更换原装线。
本文到此结束。
如果你对咱们 MASA 感兴趣,无论是代码奉献、应用、提 Issue,欢送分割咱们
WeChat:MasaStackTechOps
QQ:7424099