Flutter-BluetoothDevice库源码
源码解析
构造函数
final DeviceIdentifier remoteId; //mac地址(Android) && UUID(IOS) 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) List<BluetoothService> 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 - 连接状态流 Stream<BluetoothConnectionState> 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 Stream<int> 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 Stream<void> 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) Stream<BluetoothBondState> 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. Future<void> 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 you'll 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 don't miss the response Future<BmConnectionStateResponse> 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 weren't 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. Future<void> 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 don't miss the response //提前订阅-在调用断开方法前就开始监听,确保不会错过断开事件 Future<BmConnectionStateResponse> futureState = responseStream.first; // invoke - 执行断开操作-调用原生平台的断开方法 bool changed = await FlutterBluePlus._invokeMethod('disconnect', remoteId.str); // only wait for disconnection if weren't 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 Future<List<BluetoothService>> 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(); List<BluetoothService> 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 don't miss the response //提前订阅通知 Future<BmDiscoverServicesResult> 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,值越大越好 Future<int> 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 don't miss the response Future<BmReadRssiResult> 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值,延迟事件,超时时间 Future<int> 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 don't miss the response Future<int> 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) //请求更新连接优先级 Future<void> 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. Future<void> 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. Future<void> 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 don't miss the response Future<BmBondStateResponse> futureResponse = responseStream.first; // invoke bool changed = await FlutterBluePlus._invokeMethod('createBond', remoteId.str); // only wait for 'bonded' if we weren't 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) Future<void> 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 don't miss the response Future<BmBondStateResponse> futureResponse = responseStream.first; // invoke bool changed = await FlutterBluePlus._invokeMethod('removeBond', remoteId.str); // only wait for 'unbonded' state if we weren't 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) Future<void> 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]}' '}'; }