Flutter-BluetoothDevice库源码
源码解析构造函数final DeviceIdentifier remoteId; //mac地址Android UUIDIOS BluetoothDevice({ //构造函数 required this.remoteId, }); //命名构造函数从Protobuf协议缓冲区对象创建一个BluetoothDevice实例 //BmBluetoothDevice是生成的Protobuf类这个类用于Flutter与原生平台之间的数据传递 BluetoothDevice.fromProto(BmBluetoothDevice p) : remoteId p.remoteId; /// Create a device from an id /// - to connect, this device must have been discovered by your app in a previous scan //这个设备必须被你的APP扫描发现过才能成功连接 /// - iOS uses 128-bit uuids the remoteId, e.g. e006b3a7-ef7b-4980-a668-1f8005f84383 /// - Android uses 48-bit mac addresses as the remoteId, e.g. 06:E5:28:3B:FD:E0 BluetoothDevice.fromId(String remoteId) : remoteId DeviceIdentifier(remoteId);get方法/// platform name平台名称来自GAP服务 /// - this name is kept track of by the platform /// - this name usually persist between app restarts应用重启后还存在 /// - iOS: after you connect, iOS uses the GAP name characteristic (0x2A00) /// if it exists. Otherwise iOS use the advertised name. /// - Android: always uses the advertised name String get platformName FlutterBluePlus._platformNames[remoteId] ?? ; /// Advertised Named广播名称来自广播包 /// - this is the name advertised by the device during scanning /// - it is only available after you scan with FlutterBluePlus /// - it is cleared when the app restarts.应用重启后丢失 /// - not all devices advertise a name String get advName FlutterBluePlus._advNames[remoteId] ?? ; // iOS 的特殊行为iOS 连接后会自动读取 GAP 名称并缓存即使设备断开platformName 仍然可用 // Android 的行为Android 主要依赖广播名称platformName 通常和 advName 相同除非手动读取 GAP 服务 /// Get services /// - returns empty if discoverServices() has not been called /// or if your device does not have any services (rare) ListBluetoothService get servicesList { BmDiscoverServicesResult? result FlutterBluePlus._knownServices[remoteId];//找服务 if (result null) { return []; } else { return result.services.map((p) BluetoothService.fromProto(p)).toList(); } } /// Returns true if autoConnect is currently enabled for this device bool get isAutoConnectEnabled { //是否开启了自动重连 return FlutterBluePlus._autoConnect.contains(remoteId); } /// Returns true if this device is currently connected to your app bool get isConnected { //是否已经连接 if (FlutterBluePlus._connectionStates[remoteId] null) { return false; } else { var state FlutterBluePlus._connectionStates[remoteId]!.connectionState; return state BmConnectionStateEnum.connected; } } /// Returns true if this device is currently disconnected from your app bool get isDisconnected isConnected false; //是否已经断连 /// The most recent disconnection reason - 最近一次断开连接的原因 DisconnectReason? get disconnectReason { if (FlutterBluePlus._connectionStates[remoteId] null) { //设备是否有连接记录 return null; } //从缓存的连接状态中提取断开原因码和描述封装成DisconnectReason对象返回 int? code FlutterBluePlus._connectionStates[remoteId]!.disconnectReasonCode; String? description FlutterBluePlus._connectionStates[remoteId]!.disconnectReasonString; return DisconnectReason(_nativeError, code, description); } /// The current connection state *of our app* to the device - 连接状态流 StreamBluetoothConnectionState get connectionState { // initial value - Note: we only care about the current connection state of // *our* app, which is why we can use our cached value, or assume disconnected //获取缓存的初始值 BluetoothConnectionState initialValue BluetoothConnectionState.disconnected; if (FlutterBluePlus._connectionStates[remoteId] ! null) { initialValue _bmToConnectionState(FlutterBluePlus._connectionStates[remoteId]!.connectionState); } //监听原生层的变化事件 return FlutterBluePlus._methodStream.stream .where((m) m.method OnConnectionStateChanged) //过滤连接状态变化事件 .map((m) m.arguments) //提取参数 .map((args) BmConnectionStateResponse.fromMap(args)) //反序列化 .where((p) p.remoteId remoteId) //只关注当前设备 .map((p) _bmToConnectionState(p.connectionState));//转化为Dart枚举 //.newStreamWithInitialValue(initialValue); //一监听连接状态流会自动发送一个默认状态 } /// The current MTU size in bytes - 当前MTU值 int get mtuNow { // get initial value from our cache return FlutterBluePlus._mtuValues[remoteId]?.mtu ?? 23; } /// Stream emits a value: /// - immediately when first listened to /// - whenever the mtu changes Streamint get mtu { //MTU变化流 // get initial value from our cache int initialValue FlutterBluePlus._mtuValues[remoteId]?.mtu ?? 23; //初始值 return FlutterBluePlus._methodStream.stream .where((m) m.method OnMtuChanged) //监听MTU变化事件 .map((m) m.arguments) .map((args) BmMtuChangedResponse.fromMap(args)) .where((p) p.remoteId remoteId)//只关注当前设备 .map((p) p.mtu) //提取MTU值 .newStreamWithInitialValue(initialValue);//订阅时立即发送当前值 } /// Services Reset Stream - 服务重置流 /// - uses the GAP Services Changed characteristic (0x2A05) /// - you must re-call discoverServices() when services are reset Streamvoid get onServicesReset { return FlutterBluePlus._methodStream.stream .where((m) m.method OnServicesReset) //监听服务重置事件 .map((m) m.arguments) .map((args) BmBluetoothDevice.fromMap(args)) .where((p) p.remoteId remoteId) //只关注当前设备 .map((m) null); //转为void } /// Get the current bondState of the device (Android Only) StreamBluetoothBondState get bondState async* { // check android if (Platform.isAndroid false) { throw FlutterBluePlusException(ErrorPlatform.fbp, bondState, FbpErrorCode.androidOnly.index, android-only); } // get current state if needed if (FlutterBluePlus._bondStates[remoteId] null) { var val await FlutterBluePlus._methodChannel .invokeMethod(getBondState, remoteId.str) .then((args) BmBondStateResponse.fromMap(args)); // update _bondStates if it is still null after the await if (FlutterBluePlus._bondStates[remoteId] null) { FlutterBluePlus._bondStates[remoteId] val; } } yield* FlutterBluePlus._methodStream.stream .where((m) m.method OnBondStateChanged) .map((m) m.arguments) .map((args) BmBondStateResponse.fromMap(args)) .where((p) p.remoteId remoteId) .map((p) _bmToBondState(p.bondState)) .newStreamWithInitialValue(_bmToBondState(FlutterBluePlus._bondStates[remoteId]!.bondState)); } /// Get the previous bondState of the device (Android Only) BluetoothBondState? get prevBondState { var b FlutterBluePlus._bondStates[remoteId]?.prevState; return b ! null ? _bmToBondState(b) : null; } /// Get the Services Changed characteristic (0x2A05) BluetoothCharacteristic? get _servicesChangedCharacteristic { final Guid gattUuid Guid(1801); final Guid servicesChangedUuid Guid(2A05); BluetoothService? gatt servicesList._firstWhereOrNull((svc) svc.uuid gattUuid); return gatt?.characteristics._firstWhereOrNull((chr) chr.uuid servicesChangedUuid); }设备断开时自动取消订阅/// Register a subscription to be canceled when the device is disconnected. /// This function simplifies cleanup, to prevent creating duplicate stream subscriptions. /// - this is an optional convenience function /// - prevents accidentally creating duplicate subscriptions on each reconnection. /// - [next] if true, the the stream will be canceled only on the *next* disconnection. /// This is useful if you setup your subscriptions before you connect. /// - [delayed] Note: This option is only meant for connectionState subscriptions. /// When true, we cancel after a small delay. This ensures the connectionState /// listener receives the disconnected event. // 设备断开时自动取消订阅 void cancelWhenDisconnected(StreamSubscription subscription, {bool next false, bool delayed false}) { if (isConnected false next false) { subscription.cancel(); // cancel immediately if already disconnected. } else if (delayed) { FlutterBluePlus._delayedSubscriptions[remoteId] ?? []; FlutterBluePlus._delayedSubscriptions[remoteId]!.add(subscription); } else { FlutterBluePlus._deviceSubscriptions[remoteId] ?? []; FlutterBluePlus._deviceSubscriptions[remoteId]!.add(subscription); } }连接方法/// Establishes a connection to the Bluetooth Device. /// [timeout] if timeout occurs, cancel the connection request and throw exception /// [mtu] Android only. Request a larger mtu right after connection, if set. /// [autoConnect] reconnect whenever the device is found /// - if true, this function always returns immediately. /// - you must listen to connectionState to know when connection occurs. /// - auto connect is turned off by calling disconnect /// - auto connect results in a slower connection process compared to a direct connection /// because it relies on the internal scheduling of background scans. Futurevoid connect({ Duration timeout const Duration(seconds: 35), //连接超时范围 int? mtu 512, //Android设置MTU bool autoConnect false, //是否自动重连 }) async { // If you hit this assert, you must set mtu:null, i.e device.connect(mtu:null, autoConnect:true) // and youll have to call requestMtu yourself. autoConnect is not compatibile with mtu. //参数检查,mtu和autoConnect不能同时使用 assert((mtu null) || !autoConnect, mtu and auto connect are incompatible); // make sure no one else is calling disconnect //互斥锁保护防止连接和断连同时执行 _Mutex dmtx _MutexFactory.getMutexForKey(disconnect); bool dtook await dmtx.take(); // Only allow a single ble operation to be underway at a time //_Mutex mtx _MutexFactory.getMutexForKey(global); //await mtx.take(); try { // remember auto connect value记录自动重连标记 if (autoConnect) { FlutterBluePlus._autoConnect.add(remoteId); } var request BmConnectRequest(//构造请求参数发生给原生平台数据 remoteId: remoteId, autoConnect: autoConnect, ); //设置响应监听器 - 准备监听连接状态变化 //原生平台 → MethodChannel → _methodStream → 过滤出连接状态事件 → 过滤出本设备 → 取第一个 var responseStream FlutterBluePlus._methodStream.stream .where((m) m.method OnConnectionStateChanged) .map((m) m.arguments) .map((args) BmConnectionStateResponse.fromMap(args)) .where((p) p.remoteId remoteId); // Start listening now, before invokeMethod, to ensure we dont miss the response FutureBmConnectionStateResponse futureState responseStream.first; // invoke调用原生连接方法 bool changed await FlutterBluePlus._invokeMethod(connect, request.toMap()); // we return the disconnect mutex now so that this // connection attempt can be canceled by calling disconnect dtook dmtx.give(); //释放断开锁 // only wait for connection if we werent already connected if (changed !autoConnect) { //处理非自动连接的情况 BmConnectionStateResponse response await futureState .fbpEnsureAdapterIsOn(connect) //确保蓝牙已经开启 .fbpTimeout(timeout.inSeconds, connect) //设置超时 .catchError((e) async { if (e is FlutterBluePlusException e.code FbpErrorCode.timeout.index) { await FlutterBluePlus._invokeMethod(disconnect, remoteId.str); // cancel connection attempt } throw e; }); // failure?检查连接失败 if (response.connectionState BmConnectionStateEnum.disconnected) { // if (response.disconnectReasonCode 23789258) { // throw FlutterBluePlusException( // ErrorPlatform.fbp, connect, FbpErrorCode.connectionCanceled.index, connection canceled); // } else { // throw FlutterBluePlusException( // _nativeError, connect, response.disconnectReasonCode, response.disconnectReasonString); // } } } } finally { if (dtook) { dmtx.give(); } // mtx.give(); } // request larger mtu请求更大的MTU if (Platform.isAndroid isConnected mtu ! null) { await requestMtu(mtu); } }连接的流程图开始连接 ↓ 检查参数mtu 和 autoConnect 不能同时用 ↓ 获取互斥锁防止和 disconnect 冲突 ↓ [autoConnect true] → 加入自动重连名单 ↓ 构建请求参数 ↓ 开始监听连接状态避免错过响应 ↓ 调用原生代码连接 ↓ 释放互斥锁允许 disconnect ↓ ┌─────────────────────────────────┐ │ autoConnect false? │ └─────────────────────────────────┘ ↓ 是 ↓ 否 等待连接结果 立即返回 检查超时 不等结果 超时则自动断开 ↓ 连接成功 ↓ [Android mtu不为空] → 请求更大 MTU ↓ 结束断联方法/// Cancels connection to the Bluetooth Device /// - [queue] If true, this disconnect request will be executed after all other operations complete. /// If false, this disconnect request will be executed right now, i.e. skipping to the front /// of the fbp operation queue, which is useful to cancel an in-progress connection attempt. Futurevoid disconnect({int timeout 35, bool queue true}) async { // Only allow a single disconnect operation at a time //互斥锁控制并发-确保同时只有一个disconnect操作执行 _Mutex dtx _MutexFactory.getMutexForKey(disconnect); await dtx.take(); // Only allow a single ble operation to be underway at a time? // queue false,立即执行跳过队列 _Mutex mtx _MutexFactory.getMutexForKey(global); if (queue) { //等待所有其他蓝牙操作完成后再执行断开 await mtx.take(); } try { // remove from auto connect list if there - 取消自动重连 FlutterBluePlus._autoConnect.remove(remoteId); //监听断开状态变化 var responseStream FlutterBluePlus._methodStream.stream .where((m) m.method OnConnectionStateChanged) .map((m) m.arguments) .map((args) BmConnectionStateResponse.fromMap(args)) .where((p) p.remoteId remoteId) .where((p) p.connectionState BmConnectionStateEnum.disconnected); // Start listening now, before invokeMethod, to ensure we dont miss the response //提前订阅-在调用断开方法前就开始监听确保不会错过断开事件 FutureBmConnectionStateResponse futureState responseStream.first; // invoke - 执行断开操作-调用原生平台的断开方法 bool changed await FlutterBluePlus._invokeMethod(disconnect, remoteId.str); // only wait for disconnection if werent already disconnected if (changed) { //等待断开完成 await futureState.fbpEnsureAdapterIsOn(disconnect).fbpTimeout(timeout, disconnect); } } finally { dtx.give(); if (queue) { mtx.give(); } } }使用场景// 正常断开排队等待其他操作完成 await device.disconnect(); // 紧急取消正在进行的连接尝试不排队 await device.disconnect(queue: false, timeout: 10); // 自定义超时时间 await device.disconnect(timeout: 20);发现服务/// Discover services, characteristics, and descriptors of the remote device /// - [subscribeToServicesChanged] Android Only: If true, after discovering services we will subscribe /// to the Services Changed Characteristic (0x2A05) used for the device.onServicesReset stream. /// Note: this behavior happens automatically on iOS and cannot be disabled FutureListBluetoothService discoverServices({ bool subscribeToServicesChanged true, //Android专用订阅服务变化通知 int timeout 15 //超时时间 }) async { // check connected - 连接状态检查 if (isDisconnected) { //断开则抛出异常 throw FlutterBluePlusException( ErrorPlatform.fbp, discoverServices, FbpErrorCode.deviceIsDisconnected.index, device is not connected); } // Only allow a single ble operation to be underway at a time //全局互斥锁 - 确保同一时间只有一个蓝牙操作执行 _Mutex mtx _MutexFactory.getMutexForKey(global); await mtx.take(); ListBluetoothService result []; try { //创造响应监听流 var responseStream FlutterBluePlus._methodStream.stream .where((m) m.method OnDiscoveredServices) //匹配方法名 .map((m) m.arguments) //提取参数 .map((args) BmDiscoverServicesResult.fromMap(args)) //反序列化 .where((p) p.remoteId remoteId); //过滤当前设备 // Start listening now, before invokeMethod, to ensure we dont miss the response //提前订阅通知 FutureBmDiscoverServicesResult futureResponse responseStream.first; // invoke - 触发原生发现服务 - 调用原生平台监听避免事件丢失 await FlutterBluePlus._invokeMethod(discoverServices, remoteId.str); // wait for response - 等待响应并处理 BmDiscoverServicesResult response await futureResponse .fbpEnsureAdapterIsOn(discoverServices) //确保蓝牙开启 .fbpEnsureDeviceIsConnected(this, discoverServices) //确保设备连接 .fbpTimeout(timeout, discoverServices); //超时控制 // failed? if (!response.success) {// 失败则抛出异常 throw FlutterBluePlusException(_nativeError, discoverServices, response.errorCode, response.errorString); } //成功则将protobuf格式转换为Dart对象 result response.services.map((p) BluetoothService.fromProto(p)).toList(); } finally { mtx.give(); } // in order to match iOS behavior on all platforms, // we always listen to the Services Changed characteristic if it exists. if (subscribeToServicesChanged) { //服务变化 if (Platform.isIOS false Platform.isMacOS false) { //筛选非IOS设备 BluetoothCharacteristic? c _servicesChangedCharacteristic; //服务特征 //存在支持通知还没订阅 if (c ! null (c.properties.notify || c.properties.indicate) c.isNotifying false) { await c.setNotifyValue(true); //订阅通知 } } } return result; }获取rssi/// Read the RSSI of connected remote device //RSSI值负整数值的范围通常 -100 dBm 到 0 dBm值越大越好 Futureint readRssi({int timeout 15}) async { // check connected if (isDisconnected) { //连接状态检查 throw FlutterBluePlusException( ErrorPlatform.fbp, readRssi, FbpErrorCode.deviceIsDisconnected.index, device is not connected); } // Only allow a single ble operation to be underway at a time //全局互斥锁确保同一时间只有一个蓝牙操作 _Mutex mtx _MutexFactory.getMutexForKey(global); await mtx.take(); int rssi 0; try { //创建响应监听流 var responseStream FlutterBluePlus._methodStream.stream .where((m) m.method OnReadRssi) //匹配RSSI读取完成事件 .map((m) m.arguments) //提取参数 .map((args) BmReadRssiResult.fromMap(args)) //反序列化 .where((p) (p.remoteId remoteId)); /只关心当前设备 // Start listening now, before invokeMethod, to ensure we dont miss the response FutureBmReadRssiResult futureResponse responseStream.first;//提前订阅 // invoke - 调用原生平台的API读取RSSI await FlutterBluePlus._invokeMethod(readRssi, remoteId.str); // wait for response - 等待响应并处理 BmReadRssiResult response await futureResponse .fbpEnsureAdapterIsOn(readRssi) //确保蓝牙设配器开启 .fbpEnsureDeviceIsConnected(this, readRssi) //确保设备仍连接 .fbpTimeout(timeout, readRssi); //超时保护 // failed? if (!response.success) { //原生操作失败则抛出异常 throw FlutterBluePlusException(_nativeError, readRssi, response.errorCode, response.errorString); } rssi response.rssi; //成功则提取RSSI值 } finally { mtx.give(); //释放互斥锁 } return rssi; }请求改变MTU大小/// Request to change MTU (Android Only) - 请求改变MTU /// - returns new MTU - 返回新的MTU /// - [predelay] adds delay to avoid race conditions on some devices. see comments below. //参数期望的MTU值延迟事件超时时间 Futureint requestMtu(int desiredMtu, {double predelay 0.35, int timeout 15}) async { // check android - 过滤非安卓设备 if (Platform.isAndroid false) { throw FlutterBluePlusException(ErrorPlatform.fbp, requestMtu, FbpErrorCode.androidOnly.index, android-only); } // check connected - 检查连接状态 if (isDisconnected) { throw FlutterBluePlusException( ErrorPlatform.fbp, requestMtu, FbpErrorCode.deviceIsDisconnected.index, device is not connected); } // Only allow a single ble operation to be underway at a time //全局互斥锁 _Mutex mtx _MutexFactory.getMutexForKey(global); await mtx.take(); // predelay - 预延迟机制延迟执行requestMtu if (predelay 0) { //通过这个延迟我们可以避免导致discoverServices超时或失败的竞态条件 // hack: By adding delay before we call requestMtu, we can avoid // a race condition that can cause discoverServices to timeout or fail. //注意这个技巧仅适用于那些在连接后自动发送MTU更新的设备 //如果你的设备不会这样做你可以将此延迟设置为0 // Note: This hack is only needed for devices that automatically send an // MTU update right after connection. If your device does not do that, // you can set this delay to zero. Other people may need to increase it! //竞态条件的发送过程如下 //1.你在连接后立即调用requestMtu //2.某些设备在连接后会自动发送新的MTU无需被请求 //3.你调用的requestMtu混淆了第1步和第2步的结果导致过早返回 //4.用户以为requestMtu已经完成于是调用discoverServices //5.实际上requestMtu仍在进行中导致discoverServices调用失败或超时 // The race condition goes like this: // 1. you call requestMtu right after connection // 2. some devices automatically send a new MTU right after connection, without being asked // 3. your call to requestMtu confuses the results from step 1 and step 2, and returns to early // 4. the user then calls discoverServices, thinking that requestMtu has finished // 5. in reality, requestMtu is still happening, and the call to discoverServices will fail/timeout // // Adding delay before we call requestMtu helps ensure // that the automatic mtu update has already happened. await Future.delayed(Duration(milliseconds: (predelay * 1000).toInt())); } var mtu 0; //创建请求对象 try { var request BmMtuChangeRequest( remoteId: remoteId, mtu: desiredMtu, ); //监听MTU变化响应 var responseStream FlutterBluePlus._methodStream.stream .where((m) m.method OnMtuChanged) .map((m) m.arguments) .map((args) BmMtuChangedResponse.fromMap(args)) .where((p) p.remoteId remoteId) .map((p) p.mtu); // Start listening now, before invokeMethod, to ensure we dont miss the response Futureint futureResponse responseStream.first; // invoke - 执行MTU请求 await FlutterBluePlus._invokeMethod(requestMtu, request.toMap()); // wait for response - 等待并返回结果 mtu await futureResponse .fbpEnsureAdapterIsOn(requestMtu) .fbpEnsureDeviceIsConnected(this, requestMtu) .fbpTimeout(timeout, requestMtu); } finally { mtx.give(); } return mtu; }请求更新连接优先级/// Request connection priority update (Android only) //请求更新连接优先级 Futurevoid requestConnectionPriority({required ConnectionPriority connectionPriorityRequest}) async { // check android - 筛选安卓设备 if (Platform.isAndroid false) { throw FlutterBluePlusException( ErrorPlatform.fbp, requestConnectionPriority, FbpErrorCode.androidOnly.index, android-only); } // check connected - 检查连接 if (isDisconnected) { throw FlutterBluePlusException(ErrorPlatform.fbp, requestConnectionPriority, FbpErrorCode.deviceIsDisconnected.index, device is not connected); } //构建请求对象 var request BmConnectionPriorityRequest( remoteId: remoteId, connectionPriority: _bmFromConnectionPriority(connectionPriorityRequest), ); // invoke - 调用原生方法 await FlutterBluePlus._invokeMethod(requestConnectionPriority, request.toMap()); }使用示例// 场景1需要快速传输大量数据如 OTA 升级 await device.requestConnectionPriority( connectionPriorityRequest: ConnectionPriority.highPriority ); // 场景2日常数据传输平衡模式和功耗 await device.requestConnectionPriority( connectionPriorityRequest: ConnectionPriority.balanced ); // 场景3后台传感器数据采集省电模式 await device.requestConnectionPriority( connectionPriorityRequest: ConnectionPriority.lowPower );setPreferredPhy/// Set the preferred connection (Android Only) /// - [txPhy] bitwise OR of all allowed phys for Tx, e.g. (Phy.le2m.mask | Phy.leCoded.mask) /// - [txPhy] bitwise OR of all allowed phys for Rx, e.g. (Phy.le2m.mask | Phy.leCoded.mask) /// - [option] preferred coding to use when transmitting on Phy.leCoded /// Please note that this is just a recommendation given to the system. Futurevoid setPreferredPhy({ required int txPhy, required int rxPhy, required PhyCoding option, }) async { // check android if (Platform.isAndroid false) { throw FlutterBluePlusException( ErrorPlatform.fbp, setPreferredPhy, FbpErrorCode.androidOnly.index, android-only); } // check connected if (isDisconnected) { throw FlutterBluePlusException( ErrorPlatform.fbp, setPreferredPhy, FbpErrorCode.deviceIsDisconnected.index, device is not connected); } var request BmPreferredPhy( remoteId: remoteId, txPhy: txPhy, rxPhy: rxPhy, phyOptions: option.index, ); // invoke await FlutterBluePlus._invokeMethod(setPreferredPhy, request.toMap()); }createBond/// Force the bonding popup to show now (Android Only) /// Note! calling this is usually not necessary!! The platform does it automatically. Futurevoid createBond({int timeout 90}) async { // check android if (Platform.isAndroid false) { throw FlutterBluePlusException(ErrorPlatform.fbp, createBond, FbpErrorCode.androidOnly.index, android-only); } // check connected // if (isDisconnected) { // throw FlutterBluePlusException( // ErrorPlatform.fbp, createBond, FbpErrorCode.deviceIsDisconnected.index, device is not connected); // } // Only allow a single ble operation to be underway at a time _Mutex mtx _MutexFactory.getMutexForKey(global); await mtx.take(); try { var responseStream FlutterBluePlus._methodStream.stream .where((m) m.method OnBondStateChanged) .map((m) m.arguments) .map((args) BmBondStateResponse.fromMap(args)) .where((p) p.remoteId remoteId) .where((p) p.bondState ! BmBondStateEnum.bonding); // Start listening now, before invokeMethod, to ensure we dont miss the response FutureBmBondStateResponse futureResponse responseStream.first; // invoke bool changed await FlutterBluePlus._invokeMethod(createBond, remoteId.str); // only wait for bonded if we werent already bonded if (changed) { BmBondStateResponse bs await futureResponse .fbpEnsureAdapterIsOn(createBond) .fbpEnsureDeviceIsConnected(this, createBond) .fbpTimeout(timeout, createBond); // success? if (bs.bondState ! BmBondStateEnum.bonded) { throw FlutterBluePlusException(ErrorPlatform.fbp, createBond, FbpErrorCode.createBondFailed.hashCode, Failed to create bond. ${bs.bondState}); } } } finally { mtx.give(); } }removeBond/// Remove bond (Android Only) Futurevoid removeBond({int timeout 30}) async { // check android if (Platform.isAndroid false) { throw FlutterBluePlusException(ErrorPlatform.fbp, removeBond, FbpErrorCode.androidOnly.index, android-only); } // Only allow a single ble operation to be underway at a time _Mutex mtx _MutexFactory.getMutexForKey(global); await mtx.take(); try { var responseStream FlutterBluePlus._methodStream.stream .where((m) m.method OnBondStateChanged) .map((m) m.arguments) .map((args) BmBondStateResponse.fromMap(args)) .where((p) p.remoteId remoteId) .where((p) p.bondState ! BmBondStateEnum.bonding); // Start listening now, before invokeMethod, to ensure we dont miss the response FutureBmBondStateResponse futureResponse responseStream.first; // invoke bool changed await FlutterBluePlus._invokeMethod(removeBond, remoteId.str); // only wait for unbonded state if we werent already unbonded if (changed) { BmBondStateResponse bs await futureResponse .fbpEnsureAdapterIsOn(removeBond) .fbpEnsureDeviceIsConnected(this, removeBond) .fbpTimeout(timeout, removeBond); // success? if (bs.bondState ! BmBondStateEnum.none) { throw FlutterBluePlusException(ErrorPlatform.fbp, createBond, FbpErrorCode.removeBondFailed.hashCode, Failed to remove bond. ${bs.bondState}); } } } finally { mtx.give(); } }clearGattCache/// Refresh ble services characteristics (Android Only) Futurevoid clearGattCache() async { // check android if (Platform.isAndroid false) { throw FlutterBluePlusException( ErrorPlatform.fbp, clearGattCache, FbpErrorCode.androidOnly.index, android-only); } // check connected if (isDisconnected) { // throw FlutterBluePlusException( // ErrorPlatform.fbp, clearGattCache, FbpErrorCode.deviceIsDisconnected.index, device is not connected); } // invoke await FlutterBluePlus._invokeMethod(clearGattCache, remoteId.str); }其他override bool operator (Object other) identical(this, other) || (other is BluetoothDevice runtimeType other.runtimeType remoteId other.remoteId); override int get hashCode remoteId.hashCode; override String toString() { return BluetoothDevice{ remoteId: $remoteId, platformName: $platformName, services: ${FlutterBluePlus._knownServices[remoteId]} }; }