我的项目背景
MAUI的呈现,赋予了宽广Net开发者开发多平台利用的能力,MAUI 是Xamarin.Forms演变而来,然而相比Xamarin性能更好,可扩展性更强,构造更简略。然而MAUI对于平台相干的实现并不残缺。所以MASA团队发展了一个实验性我的项目,意在对微软MAUI的补充和扩大,我的项目地址:https://github.com/BlazorComp...,每个性能都有独自的demo演示我的项目,思考到app安装文件体积(尽管MAUI曾经集成裁剪性能,然而该性能对于代码自身有影响),届时每一个性能都会以独自的nuget包的模式提供,不便测试,当初我的项目才刚刚开始,然而置信很快就会有能够交付的内容啦。
前言
本系列文章面向挪动开发小白,从零开始进行平台相干性能开发,演示如何参考平台的官网文档应用MAUI技术来开发相应性能。
介绍
微软的MAUI并没有提供蓝牙低功耗设施的相干性能,而物联网开发中蓝牙低功耗是非常常见的,所以咱们明天本人集成一个。
因为蓝牙功能设计的内容比拟多,篇幅无限,本文只集成一个最根本的蓝牙扫描性能,意在抛砖引玉。后续会陆续更新其余蓝牙通信性能的文章。本文蓝牙低功耗简称为BLE
如果你对BLE的相干概念不理解,能够参考 开发者官网链接: 蓝牙低功耗-安卓
https://developer.android.goo...
本文JAVA相干代码均来自安卓开发者官网
开发步骤
新建我的项目
在vs中新建一个基于MAUI Blazor的我的项目MauiBlueToothDemo,而后增加一个MAUI类库我的项目Masa.Maui.Plugin.Bluetooth
增加权限
我的项目创立好了之后,咱们首先介绍一下BLE须要的安卓权限,置信大家对各种APP首次关上的权限确认弹窗应该不会生疏。
在利用中应用蓝牙性能,必须申明 BLUETOOTH 蓝牙权限,须要此权限能力执行任何蓝牙通信,例如申请连贯、承受连贯和传输数据等。
因为 LE 信标通常与地位相关联,还须申明 ACCESS_FINE_LOCATION 权限。没有此权限,扫描将无奈返回任何后果。
如果适配 Android 9(API 级别 28)或更低版本,能够申明 ACCESS_COARSE_LOCATION 权限而非 ACCESS_FINE_LOCATION 权限
如果想让利用启动设施发现或操纵蓝牙设置,还须申明 BLUETOOTH_ADMIN 权限。留神:如果应用 LUETOOTH_ADMIN 权限,则您必须领有 BLUETOOTH 权限。
在MauiBlueToothDemo我的项目中的AndroidManifest.xml增加权限,咱们这里面向Android 9以上版本。
<!--蓝牙权限--> <uses-permission android:name="android.permission.BLUETOOTH" /> <!--让利用启动设施发现或操纵蓝牙设置--> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <!-- 如果设配Android9及更低版本,能够申请 ACCESS_COARSE_LOCATION --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Android 6.0之后,只在AndroidManifest.xml申明权限曾经不够了,出于平安思考,必须动静申请权限,也就是须要在应用特定性能之前提醒用户进行权限确认。
咱们在Masa.Maui.Plugin.Bluetooth我的项目的Platforms_Android下新建MasaMauiBluetoothService类,并增加一个外部类BluetoothPermissions ,MAUI的默认权限没有蕴含蓝牙低功耗,所以咱们须要扩大一个自定义的蓝牙权限类,只有继承自 Permissions.BasePermission即可
private class BluetoothPermissions : Permissions.BasePlatformPermission { public override (string androidPermission, bool isRuntime)[] RequiredPermissions => new List<(string androidPermission, bool isRuntime)> { (global::Android.Manifest.Permission.AccessFineLocation, true), (global::Android.Manifest.Permission.Bluetooth, true), (global::Android.Manifest.Permission.BluetoothAdmin, true), }.ToArray(); }
咱们在MasaMauiBluetoothService类外部增加一个办法,来实现动静获取权限
public async Task<bool> CheckAndRequestBluetoothPermission() { var status = await Permissions.CheckStatusAsync<BluetoothPermissions>(); if (status == PermissionStatus.Granted) return true; status = await Permissions.RequestAsync<BluetoothPermissions>(); if (status == PermissionStatus.Granted) return true; return false; }
查看权限的以后状态,应用 Permissions.CheckStatusAsync 办法。
向用户申请权限,应用 Permissions.RequestAsync 办法。 如果用户以前授予了权限,并且尚未吊销该权限,则此办法将返回 Granted 而不向用户显示对话框。
设置BLE
BLE的开发第一步骤就是设置BLE
为什么要设置BLE,因为咱们在应用BLE进行通信之前,须要验证设施是否反对BLE或者查看BLE是否开启。咱们先看一下java的实现形式
JAVA 代码private BluetoothAdapter bluetoothAdapter;...// Initializes Bluetooth adapter.final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);bluetoothAdapter = bluetoothManager.getAdapter();
在编写平台相干代码时,安卓的系统管理服务都是同getSystemService办法获取的,该办法的参数为零碎服务的名称,对应在MAUI中的办法为Android.App.Application.Context.GetSystemService,流程是齐全一样的,语法稍有不同,咱们如法炮制,在MasaMauiBluetoothService中增加一个构造函数,和两个字段
private readonly BluetoothManager _bluetoothManager; private readonly BluetoothAdapter _bluetoothAdapter; public MasaMauiBluetoothService() { _bluetoothManager = (BluetoothManager)Android.App.Application.Context.GetSystemService(Android.App.Application.BluetoothService); _bluetoothAdapter = _bluetoothManager?.Adapter; }
GetSystemService返回BluetoothManager 实例,而后通过BluetoothManager 获取BluetoothAdapter, BluetoothAdapter代表设施本身的蓝牙适配器,之后的蓝牙操作都须要通过BluetoothAdapter实现
持续在MasaMauiBluetoothService增加一个查看蓝牙适配器是否存在并开启的办法
public bool IsEnabled() { return _bluetoothAdapter is {IsEnabled: true}; }
BLE扫描
与BLE设施通信,首先须要扫描出左近的BLE设施,咱们先看看Java怎么实现的
JAVA 代码/** * Activity for scanning and displaying available BLE devices. */public class DeviceScanActivity extends ListActivity { private BluetoothAdapter bluetoothAdapter; private boolean mScanning; private Handler handler; // Stops scanning after 10 seconds. private static final long SCAN_PERIOD = 10000; ... private void scanLeDevice(final boolean enable) { if (enable) { // Stops scanning after a pre-defined scan period. handler.postDelayed(new Runnable() { @Override public void run() { mScanning = false; bluetoothAdapter.stopLeScan(leScanCallback); } }, SCAN_PERIOD); mScanning = true; bluetoothAdapter.startLeScan(leScanCallback); } else { mScanning = false; bluetoothAdapter.stopLeScan(leScanCallback); } ... }...}
扫描设施须要应用bluetoothAdapter.startLeScan办法,并指定一个BluetoothAdapter.LeScanCallback回调办法作为参数
咱们再看一下LeScanCallback的Java实现
JAVA 代码private LeDeviceListAdapter leDeviceListAdapter;...// Device scan callback.private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { runOnUiThread(new Runnable() { @Override public void run() { leDeviceListAdapter.addDevice(device); leDeviceListAdapter.notifyDataSetChanged(); } }); }};
因为扫描很消耗资源,所以示例代码通过runOnUiThread设置扫描过程在设施的前台运行,扫描到设施后触发leScanCallback 回调,而后通过公有的LeDeviceListAdapter字段保留扫描到的设施列表。
咱们如法炮制这部分性能,在MasaMauiBluetoothService中增加一个继承自ScanCallback外部类DevicesCallback,ScanCallback类 对应安卓的leScanCallback
private class DevicesCallback : ScanCallback { 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 OnScanResult(ScanCallbackType callbackType, ScanResult result) { System.Diagnostics.Debug.WriteLine("OnScanResult"); if (!Devices.Contains(result.Device)) { Devices.Add(result.Device); } base.OnScanResult(callbackType, result); } }
篇幅问题咱们这里只重写OnScanResult一个办法。当有设施被扫描到就会触发这个办法,而后就能够通过ScanResult的Device属性来获取设施信息。
咱们在MAUI中打印调试信息能够应用System.Diagnostics.Debug.WriteLine真机调试的信息会被打印到vs的输入控制台。
咱们增加一个属性Devices用于汇总收集扫描到的设施信息。这里应用了EventWaitHandle 用于在异步操作时控制线程间的同步,线程在 EventWaitHandle 上将始终碰壁,直到未受阻的线程调用 Set 办法,没用过的能够自行查看微软文档。
持续在MasaMauiBluetoothService增加字段,并在构造函数初始化。
private readonly ScanSettings _settings; private readonly DevicesCallback _callback; public MasaMauiBluetoothService() { _bluetoothManager = (BluetoothManager)Android.App.Application.Context.GetSystemService(Android.App.Application.BluetoothService); _bluetoothAdapter = _bluetoothManager?.Adapter; _settings = new ScanSettings.Builder() .SetScanMode(Android.Bluetooth.LE.ScanMode.Balanced) ?.Build(); _callback = new DevicesCallback(); }
这里也很好了解,ScanSettings通过ScanSettings.Builder() 结构,用来配置蓝牙的扫描模式,咱们这里应用均衡模式,具体式有如下三种:
ScanSettings.SCAN_MODE_LOW_POWER 低功耗模式(默认扫描模式,如果扫描应用程序不在前台,则强制应用此模式。)
ScanSettings.SCAN_MODE_BALANCED 均衡模式
ScanSettings.SCAN_MODE_LOW_LATENCY 高功耗模式(倡议仅在应用程序在前台运行时才应用此模式。)
最初增加ScanLeDeviceAsync办法
public async Task<IReadOnlyCollection<BluetoothDevice>> ScanLeDeviceAsync() { //第一个参数能够设置过滤条件-蓝牙名称,名称前缀,服务号等,这里临时不设置过滤条件 _bluetoothAdapter.BluetoothLeScanner.StartScan(null, _settings, _callback); await Task.Run(() => { _callback.WaitOne(); }); _bluetoothAdapter.BluetoothLeScanner.StopScan(_callback); return _callback.Devices.AsReadOnly(); }
StartScan办法的第一个参数是过滤条件,能够依据名称等进行过滤,咱们暂不设置过滤。
测试
编译Masa.Maui.Plugin.Bluetooth我的项目,而后在MauiBlueToothDemo我的项目中援用Masa.Maui.Plugin.Bluetooth.dll。
批改MauiBlueToothDemo的Index页面,页面应用了对MAUI反对良好的Masa Blazor组件: Masa Blazor
@page "/"<MButton OnClick="ScanBLEDeviceAsync">扫描蓝牙设施</MButton><div class="text-center"> <MDialog @bind-Value="ShowProgress" Width="500"> <ChildContent> <MCard> <MCardTitle> 正在扫描蓝牙设施 </MCardTitle> <MCardText> <MProgressCircular Size="40" Indeterminate Color="primary"></MProgressCircular> </MCardText> </MCard> </ChildContent> </MDialog></div><MCard Class="mx-auto" MaxWidth="400" Tile> @foreach (var item in BluetoothDeviceList) { <MListItem> <MListItemContent> <MListItemTitle>@item</MListItemTitle> </MListItemContent> </MListItem> }</MCard>
using Masa.Maui.Plugin.Bluetooth;using Microsoft.AspNetCore.Components;namespace MauiBlueToothDemo.Pages{ public partial class Index { private bool ShowProgress { get; set; } private List<string> BluetoothDeviceList { get; set; } = new(); [Inject] private MasaMauiBluetoothService BluetoothService { get; set; } private async Task ScanBLEDeviceAsync() { if (BluetoothService.IsEnabled()) { if (await BluetoothService.CheckAndRequestBluetoothPermission()) { ShowProgress = true; var deviceList = await BluetoothService.ScanLeDeviceAsync(); BluetoothDeviceList = deviceList.Where(o => !string.IsNullOrEmpty(o.Name)).Select(o => o.Name).Distinct().ToList(); ShowProgress = false; } } } }}
不要遗记在MauiProgram.cs注入写好的MasaMauiBluetoothService
#if ANDROID builder.Services.AddSingleton<MasaMauiBluetoothService>();#endif
咱们真机运行一下看看成果
同时在vs的输入中能够看到打印的日志
本文到此结束,下一篇咱们实现具体的BLE的通信。
如果你对咱们MASA感兴趣,无论是代码奉献、应用、提 Issue,欢送分割咱们
WeChat:MasaStackTechOps
QQ:7424099