FlutterBluePlus

变量
Dart 复制代码
  static bool _initialized = false; //初始化标志

  /// native platform channel - 与原生平台通信的通道
  static final MethodChannel _methodChannel = const MethodChannel('flutter_blue_plus/methods');

  /// a broadcast stream version of the MethodChannel
  // ignore: close_sinks
  //接收原平台主动发来的事件
  static final StreamController<MethodCall> _methodStream = StreamController.broadcast();

  // always keep track of these device variables
  //设备状态缓存
  static final Map<DeviceIdentifier, BmConnectionStateResponse> _connectionStates = {};
  //服务列表
  static final Map<DeviceIdentifier, BmDiscoverServicesResult> _knownServices = {};
  //配对状态
  static final Map<DeviceIdentifier, BmBondStateResponse> _bondStates = {};
  //保存MTU的值
  static final Map<DeviceIdentifier, BmMtuChangedResponse> _mtuValues = {};
  //保存设备名称
  static final Map<DeviceIdentifier, String> _platformNames = {}; //系统名称
  static final Map<DeviceIdentifier, String> _advNames = {}; //广播名称
  //最后的读写特征
  static final Map<DeviceIdentifier, Map<String, List<int>>> _lastChrs = {};
  static final Map<DeviceIdentifier, Map<String, List<int>>> _lastDescs = {};
  //订阅管理
  static final Map<DeviceIdentifier, List<StreamSubscription>> _deviceSubscriptions = {};
  static final Map<DeviceIdentifier, List<StreamSubscription>> _delayedSubscriptions = {};
  static final List<StreamSubscription> _scanSubscriptions = [];
  //自动连接集合
  static final Set<DeviceIdentifier> _autoConnect = {};

  //扫描相关
  /// stream used for the isScanning public api
  static final _isScanning = _StreamControllerReEmit<bool>(initialValue: false);

  /// stream used for the scanResults public api
  static final _scanResults = _StreamControllerReEmit<List<ScanResult>>(initialValue: []);

  /// buffers the scan results
  static _BufferStream<BmScanResponse>? _scanBuffer;

  /// the subscription to the merged scan results stream
  static StreamSubscription<BmScanResponse?>? _scanSubscription;

  /// timeout for scanning that can be cancelled by stopScan
  static Timer? _scanTimeout;

  /// the last known adapter state - 适配器状态
  static BmAdapterStateEnum? _adapterStateNow;

  /// FlutterBluePlus log level - 日志配置
  static LogLevel _logLevel = LogLevel.debug;
  static bool _logColor = true;
get方法
Dart 复制代码
 //获取当前日志级别 
 static LogLevel get logLevel => _logLevel;

  /// Checks whether the hardware supports Bluetooth - 硬件是否支持蓝牙
  static Future<bool> get isSupported async => await _invokeMethod('isSupported');

  /// The current adapter state - 当前蓝牙适配器状态 
  static BluetoothAdapterState get adapterStateNow =>
      _adapterStateNow != null ? _bmToAdapterState(_adapterStateNow!) : BluetoothAdapterState.unknown;

  /// Return the friendly Bluetooth name of the local Bluetooth adapter - 适配器名称
  static Future<String> get adapterName async => await _invokeMethod('getAdapterName');

  /// returns whether we are scanning as a stream - 扫描状态
  static Stream<bool> get isScanning => _isScanning.stream;

  /// are we scanning right now? - 当前时刻是否正在扫描
  static bool get isScanningNow => _isScanning.latestValue;

  /// the most recent scan results - 最近一次扫描的结果列表
  static List<ScanResult> get lastScanResults => _scanResults.latestValue;

  /// a stream of scan results - 作为流返回扫描结果
  //重新监听时会重新发送之前的结果
  /// - if you re-listen to the stream it re-emits the previous results
  //包含从扫描开始以来的所有结果
  /// - the list contains all the results since the scan started
  /// - the returned stream is never closed.返回的流永远不会关闭
  static Stream<List<ScanResult>> get scanResults => _scanResults.stream;

  /// This is the same as scanResults, except:
  /// - it *does not* re-emit previous results after scanning stops.
  static Stream<List<ScanResult>> get onScanResults {
    if (isScanningNow) {
      return _scanResults.stream;//正常返回扫描结果流
    } else {
      // skip previous results & push empty list
      //跳过之前的缓存结果,先发送空列表[],然后才发送新结果
      return _scanResults.stream.skip(1).newStreamWithInitialValue([]);
    }
  }

疑问点

Dart 复制代码
1.蓝牙适配器是什么,作用是什么?

答:蓝牙适配器是指设备上负责管理蓝牙功能的硬件模块及其对应的软件接口。
核心作用:管理蓝牙开关状态,设备识别与命名,扫描周围设备,管理连接。
访问蓝牙设备事件流
Dart 复制代码
 /// Get access to all device event streams
//蓝牙设备事件流的统一访问入口
  static final BluetoothEvents events = BluetoothEvents();
开启蓝牙的警告框
Dart 复制代码
//当蓝牙不可用时,系统是否自动弹出体系用户开启蓝牙的警告框  
/// Set configurable options - 只能在IOS和MacOS系统上使用
  ///   - [showPowerAlert] Whether to show the power alert (iOS & MacOS only). i.e. CBCentralManagerOptionShowPowerAlertKey
  ///       To set this option you must call this method before any other method in this package.
  ///       See: https://developer.apple.com/documentation/corebluetooth/cbcentralmanageroptionshowpoweralertkey
  ///       This option has no effect on Android.
  static Future<void> setOptions({
    bool showPowerAlert = true,
  }) async {
    await _invokeMethod('setOptions', {"show_power_alert": showPowerAlert});
  }
打开蓝牙
Dart 复制代码
  /// Turn on Bluetooth (Android only),只安卓设备有效
  static Future<void> turnOn({int timeout = 60}) async {
    //设置响应监听
    var responseStream = FlutterBluePlus._methodStream.stream
        .where((m) => m.method == "OnTurnOnResponse")
        .map((m) => m.arguments)
        .map((args) => BmTurnOnResponse.fromMap(args));

    // Start listening now, before invokeMethod, to ensure we don't miss the response
    //提前开始监听
    Future<BmTurnOnResponse> futureResponse = responseStream.first;

    // invoke - 调用原生方法
    bool changed = await _invokeMethod('turnOn');

    // only wait if bluetooth was off 只在蓝牙原本关闭时执行
    if (changed) {
      // wait for response - 等待用户选择
      BmTurnOnResponse response = await futureResponse.fbpTimeout(timeout, "turnOn");

      // check response 检查授权结果
      if (response.userAccepted == false) {
        throw FlutterBluePlusException(ErrorPlatform.fbp, "turnOn", FbpErrorCode.userRejected.index, "user rejected");
      }

      // wait for adapter to turn on - 等待蓝牙完全开启
      await adapterState.where((s) => s == BluetoothAdapterState.on).first.fbpTimeout(timeout, "turnOn");
    }
  }

交互时序图

Dart 复制代码
用户代码               Flutter层               原生层(Android)           系统对话框
   |                      |                         |                        |
   |---turnOn()---------->|                         |                        |
   |                      |---监听响应(提前)-------->|                        |
   |                      |                         |                        |
   |                      |---_invokeMethod-------->|                        |
   |                      |   ('turnOn')            |                        |
   |                      |                         |---显示对话框---------->|
   |                      |                         |                        |
   |                      |                         |<--用户点击"允许"-------|
   |                      |<--OnTurnOnResponse------|                        |
   |                      |   (userAccepted=true)   |                        |
   |                      |                         |                        |
   |                      |<--adapterState=on-------|                        |
   |<--(完成)-------------|                         |                        |
异步生成器
Dart 复制代码
  /// Gets the current state of the Bluetooth module
  //提供蓝牙适配器状态的持续监控流
  static Stream<BluetoothAdapterState> get adapterState async* {
    // get current state if needed 获取初始状态
    if (_adapterStateNow == null) {
      var result = await _invokeMethod('getAdapterState');
      var value = BmBluetoothAdapterState.fromMap(result).adapterState;
      // update _adapterStateNow if it is still null after the await
      //双重检查,防止并发问题
      if (_adapterStateNow == null) {
        _adapterStateNow = value;
      }
    }

    //创建状态变化监听流
    yield* FlutterBluePlus._methodStream.stream
        .where((m) => m.method == "OnAdapterStateChanged")
        .map((m) => m.arguments)
        .map((args) => BmBluetoothAdapterState.fromMap(args))
        .map((s) => _bmToAdapterState(s.adapterState))
        .newStreamWithInitialValue(_bmToAdapterState(_adapterStateNow!));
  }
连接的蓝牙设备列表
Dart 复制代码
  /// Retrieve a list of devices currently connected to your app
  //获取当前应用已经连接的蓝牙设备列表(当前软件连接的BLE设备)
  static List<BluetoothDevice> get connectedDevices {
    var copy = Map.from(_connectionStates); //复制连接状态映射
    //删除未连接的设备
    copy.removeWhere((key, value) => value.connectionState == BmConnectionStateEnum.disconnected);
    //转换未设备对象列表
    return copy.values.map((v) => BluetoothDevice(remoteId: v.remoteId)).toList();
  }
系统中所有已经连接的蓝牙设备
Dart 复制代码
  /// Retrieve a list of devices currently connected to the system
  /// - The list includes devices connected to by *any* app
  /// - You must still call device.connect() to connect them to *your app*
  //返回系统中所有已连接的蓝牙设备列表,不仅限于当前应用连接的设备
  //整个手机连接的BLE设备
  static Future<List<BluetoothDevice>> get systemDevices async {
    var result = await _invokeMethod('getSystemDevices');//调用原生方法
    var r = BmDevicesList.fromMap(result); //解析结果
    for (BmBluetoothDevice device in r.devices) { //缓存设备名称
      if (device.platformName != null) {
        _platformNames[device.remoteId] = device.platformName!;
      }
    }
    return r.devices.map((d) => BluetoothDevice.fromProto(d)).toList(); //转换为设备对象
  }
获取已配对的蓝牙设备列表
Dart 复制代码
  /// Retrieve a list of bonded devices (Android only)
  //获取已配对的蓝牙设备列表(连接BLE的设备+连接经典蓝牙的设备)
  static Future<List<BluetoothDevice>> get bondedDevices async {
    var result = await _invokeMethod('getBondedDevices');//调用原生方法
    var r = BmDevicesList.fromMap(result); //解析结果
    for (BmBluetoothDevice device in r.devices) { //缓存设备名称
      if (device.platformName != null) {
        _platformNames[device.remoteId] = device.platformName!;
      }
    }
    return r.devices.map((d) => BluetoothDevice.fromProto(d)).toList(); //返回设备列表
  }
扫描方法
Dart 复制代码
  /// Start a scan, and return a stream of results 开始扫描,并返回结果流
  /// Note: scan filters use an "or" behavior. i.e. if you set `withServices` & `withNames` we
  //扫描过滤器使用'或'行为
  /// return all the advertisments that match any of the specified services *or* any of the specified names.
  ///   - [withServices] filter by advertised services
  ///   - [withRemoteIds] filter for known remoteIds (iOS: 128-bit guid, android: 48-bit mac address)
  ///   - [withNames] filter by advertised names (exact match)
  ///   - [withKeywords] filter by advertised names (matches any substring)
  ///   - [withMsd] filter by manfacture specific data
  ///   - [withServiceData] filter by service data
  ///   - [timeout] calls stopScan after a specified duration
  ///   - [removeIfGone] if true, remove devices after they've stopped advertising for X duration
  ///   - [continuousUpdates] If `true`, we continually update 'lastSeen' & 'rssi' by processing
  //如果为 `true`,我们通过处理重复的广播数据来持续更新 'lastSeen' 和 'rssi'。这会消耗更多电量。通常不建议使用此选项。
  ///        duplicate advertisements. This takes more power. You typically should not use this option.
  ///   - [continuousDivisor] Useful to help performance. If divisor is 3, then two-thirds of advertisements are
  ///        ignored, and one-third are processed. This reduces main-thread usage caused by the platform channel.
  ///        The scan counting is per-device so you always get the 1st advertisement from each device.
  ///        If divisor is 1, all advertisements are returned. This argument only matters for `continuousUpdates` mode.
  ///   - [oneByOne] if `true`, we will stream every advertistment one by one, possibly including duplicates.
  ///        If `false`, we deduplicate the advertisements, and return a list of devices.
  ///   - [androidScanMode] choose the android scan mode to use when scanning
  ///   - [androidUsesFineLocation] request `ACCESS_FINE_LOCATION` permission at runtime
  static Future<void> startScan({
    //过滤参数
    List<Guid> withServices = const [], //按服务UUID过滤
    List<String> withRemoteIds = const [], //按设备ID过滤(IOS:GUID,Android:MAC地址)
    List<String> withNames = const [], //按设备名称过滤(精准匹配)
    List<String> withKeywords = const [],//按关键词过滤(子串匹配)
    List<MsdFilter> withMsd = const [], //按服务商特定数据过滤
    List<ServiceDataFilter> withServiceData = const [], //按服务数据过滤
    //行为参数
    Duration? timeout, //自动停止扫描的时长
    Duration? removeIfGone,//设备停止广播X时长后移除
    bool continuousUpdates = false,//是否持续更新重复广告
    int continuousDivisor = 1,//性能优化:每N条广告处理1条
    bool oneByOne = false,//true:逐个返回设备;false:批量返回列表
    //Android专用参数
    AndroidScanMode androidScanMode = AndroidScanMode.lowLatency,//扫描模式
    bool androidUsesFineLocation = false, //是否需要精确定位权限
  }) async {
    // check args - 参数合法性检查
    //如果设置了 removeIfGone(不为 null),则必须同时设置 continuousUpdates = true
    assert(removeIfGone == null || continuousUpdates, "removeIfGone requires continuousUpdates");
    //不能同时使用 removeIfGone 和 oneByOne = true
    assert(removeIfGone == null || !oneByOne, "removeIfGone is not compatible with oneByOne");
    //continuousDivisor 必须是大于等于 1 的整数
    assert(continuousDivisor >= 1, "divisor must be >= 1");

    // check filters - 检查是否设置了除withKeywords以外的其他过滤器
    bool hasOtherFilter = withServices.isNotEmpty ||
        withRemoteIds.isNotEmpty ||
        withNames.isNotEmpty ||
        withMsd.isNotEmpty ||
        withServiceData.isNotEmpty;

    // Note: `withKeywords` is not compatible with other filters on android
    //注意:`withKeywords` 在 Android 上与其他过滤器不兼容
    // because it is implemented in custom fbp code, not android code
    // 因为它是在自定义的 fbp 代码中实现的,而不是在 Android 原生代码中
    assert(!(Platform.isAndroid && withKeywords.isNotEmpty && hasOtherFilter),
        "withKeywords is not compatible with other filters on Android");

    // only allow a single task to call
    // startScan or stopScan at a time
    _Mutex mtx = _MutexFactory.getMutexForKey("scan"); //互斥锁控制
    await mtx.take();
    try {
      // already scanning?
      if (_isScanning.latestValue == true) {
        // stop existing scan
        await _stopScan();
      } else {
        // push to stream
        _isScanning.add(true);
      }

      //创造扫描配置对象
      var settings = BmScanSettings(
          withServices: withServices,
          withRemoteIds: withRemoteIds,
          withNames: withNames,
          withKeywords: withKeywords,
          withMsd: withMsd.map((d) => d._bm).toList(),
          withServiceData: withServiceData.map((d) => d._bm).toList(),
          continuousUpdates: continuousUpdates,
          continuousDivisor: continuousDivisor,
          androidScanMode: androidScanMode.value,
          androidUsesFineLocation: androidUsesFineLocation);

      //创建响应流监听
      Stream<BmScanResponse> responseStream = FlutterBluePlus._methodStream.stream
          .where((m) => m.method == "OnScanResponse") //只接收扫描响应
          .map((m) => m.arguments)
          .map((args) => BmScanResponse.fromMap(args));

      // Start listening now, before invokeMethod, so we do not miss any results
      _scanBuffer = _BufferStream.listen(responseStream);//开始监听

      // invoke platform method - 调用原生扫描
      await _invokeMethod('startScan', settings.toMap()).onError((e, s) => _stopScan(invokePlatform: false));

      // check every 250ms for gone devices? - 处理设备过期
      late Stream<BmScanResponse?> outputStream = removeIfGone != null
          ? _mergeStreams([_scanBuffer!.stream, Stream.periodic(Duration(milliseconds: 250))])
          : _scanBuffer!.stream;

      // start by pushing an empty array
      _scanResults.add([]);

      List<ScanResult> output = [];

      // listen & push to `scanResults` stream - 处理扫描结果
      _scanSubscription = outputStream.listen((BmScanResponse? response) {
        if (response == null) {
          // if null, this is just a periodic update to remove old results
          //定期检查:移除过期设备
          if (output._removeWhere((elm) => DateTime.now().difference(elm.timeStamp) > removeIfGone!)) {
            _scanResults.add(List.from(output)); // push to stream
          }
        } else {
          // failure? 错误处理
          if (response.success == false) {
            var e = FlutterBluePlusException(_nativeError, "scan", response.errorCode, response.errorString);
            _scanResults.addError(e);//向流中添加错误
            _stopScan(invokePlatform: false);//停止扫描,不调用原生层
          }

          // iterate through advertisements
          for (BmScanAdvertisement bm in response.advertisements) {//遍历广播数据
            // cache platform name - 缓存设备名称
            if (bm.platformName != null) {
              _platformNames[bm.remoteId] = bm.platformName!;
            }

            // cache advertised name - 缓存广播名称
            if (bm.advName != null) {
              _advNames[bm.remoteId] = bm.advName!;
            }

            // convert - 转换为ScanResult的对象
            ScanResult sr = ScanResult.fromProto(bm);

            if (oneByOne) {
              // push single item
              _scanResults.add([sr]); //每次只推送一个设备的列表
            } else {
              // add result to output
              output.addOrUpdate(sr);//更新设备列表
            }
          }

          // push entire list 批量推送
          if (!oneByOne) {
            _scanResults.add(List.from(output));
          }
        }
      });

      // Start timer *after* stream is being listened to, to make sure the
      // timeout does not fire before _scanSubscription is set
      if (timeout != null) {
        _scanTimeout = Timer(timeout, stopScan); //设置扫描超时定时器
      }
    } finally {
      mtx.give(); //解锁
    }
  }
停止扫描
Dart 复制代码
  /// Stops a scan for Bluetooth Low Energy devices 
  //给外部调用的API,负责安全的停止扫描
  static Future<void> stopScan() async {
    _Mutex mtx = _MutexFactory.getMutexForKey("scan"); //互斥锁
    await mtx.take();
    try {
      if(isScanningNow) { //只有在扫描中才停止
        await _stopScan();
      } else if (_logLevel.index >= LogLevel.info.index) {
        print("[FBP] stopScan: already stopped");
      }
    } finally {
      mtx.give(); //释放锁
    }
  }

  /// for internal use
  //内部实现的停止扫描方法
  static Future<void> _stopScan({bool invokePlatform = true}) async {
    _scanBuffer?.close(); //关闭缓冲区
    _scanSubscription?.cancel();//取消扫描订阅
    _scanTimeout?.cancel(); //取消超时定时器
    _isScanning.add(false); //更新状态为未扫描
    for (var subscription in _scanSubscriptions) {
      subscription.cancel();//取消所有其他订阅
    }
    if (invokePlatform) {
      await _invokeMethod('stopScan');//通知原生层停止
    }
  }

清理步骤

Dart 复制代码
┌─────────────────────────────────────────────────────────┐
│                    _stopScan() 清理流程                  │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  1. _scanBuffer?.close()                                │
│     └─ 关闭结果缓冲区,停止接收新数据                    │
│                                                         │
│  2. _scanSubscription?.cancel()                         │
│     └─ 取消扫描结果的订阅,停止处理                     │
│                                                         │
│  3. _scanTimeout?.cancel()                              │
│     └─ 取消超时定时器,防止超时后重复停止               │
│                                                         │
│  4. _isScanning.add(false)                              │
│     └─ 更新状态,通知所有监听者扫描已停止               │
│                                                         │
│  5. 遍历取消 _scanSubscriptions                         │
│     └─ 清理所有其他相关订阅(如各设备的连接状态)       │
│                                                         │
│  6. _invokeMethod('stopScan')                           │
│     └─ 通知原生层停止扫描(可选)                       │
│                                                         │
└─────────────────────────────────────────────────────────┘
管理流订阅的生命周期
Dart 复制代码
  /// Register a subscription to be canceled when scanning is complete.
  /// This function simplifies cleanup, to prevent creating duplicate stream subscriptions.
  ///   - this is an optional convenience function
  ///   - prevents accidentally creating duplicate subscriptions before each scan
  //注册一个订阅,在扫描完成时自动取消,这个函数简化了清理工作,防止创建重复的流订阅
  //这是一个可选的便捷函数,防止在每次扫描前意外创建重复订阅
  static void cancelWhenScanComplete(StreamSubscription subscription) {
    FlutterBluePlus._scanSubscriptions.add(subscription);
  }
设置FlutterBluePlus插件的日志级别
Dart 复制代码
  /// Sets the internal FlutterBlue log level
  //设置FlutterBluePlus内部的日志级别
  static Future<void> setLogLevel(LogLevel level, {color = true}) async {
    _logLevel = level; //保存日志级别到内存
    _logColor = color; //保存是否使用彩色日志
    await _invokeMethod('setLogLevel', level.index);//通知原生层
  }
获取蓝牙物理层(PHY)支持信息
Dart 复制代码
  /// Request Bluetooth PHY support 请求蓝牙PHY支持信息
  static Future<PhySupport> getPhySupport() async {
    // check android 过滤出安卓设备
    if (Platform.isAndroid == false) {
      throw FlutterBluePlusException(
          ErrorPlatform.fbp, "getPhySupport", FbpErrorCode.androidOnly.index, "android-only");
    }

    //调用原生方法并转换结果
    return await _invokeMethod('getPhySupport').then((args) => PhySupport.fromMap(args));
  
初始化方法
Dart 复制代码
 static Future<dynamic> _initFlutterBluePlus() async {
    if (_initialized) { 
      return;
    }

    _initialized = true;

    // set platform method handler 设置平台方法调用处理器
    _methodChannel.setMethodCallHandler(_methodCallHandler);

    // hot restart 处理热重启情况
    if ((await _methodChannel.invokeMethod('flutterHotRestart')) != 0) {
      await Future.delayed(Duration(milliseconds: 50));
      while ((await _methodChannel.invokeMethod('connectedCount')) != 0) {
        await Future.delayed(Duration(milliseconds: 50));
      }
    }
  }
方法调用处理器
Dart 复制代码
  static Future<dynamic> _methodCallHandler(MethodCall call) async {
    // log result
    if (logLevel == LogLevel.verbose) { //日志级别设置为详细模式
      String func = '[[ ${call.method} ]]'; 
      String result = call.arguments.toString();
      func = _logColor ? _black(func) : func;
      result = _logColor ? _brown(result) : result;
      print("[FBP] $func result: $result");
    }

    // android only
    if (call.method == "OnDetachedFromEngine") {
      _stopScan(invokePlatform: false);
    }

    // keep track of adapter states
    if (call.method == "OnAdapterStateChanged") {
      BmBluetoothAdapterState r = BmBluetoothAdapterState.fromMap(call.arguments);
      _adapterStateNow = r.adapterState;
      if (isScanningNow && r.adapterState != BmAdapterStateEnum.on) {
        _stopScan(invokePlatform: false);
      }
      if (r.adapterState == BmAdapterStateEnum.on) {
        for (DeviceIdentifier d in _autoConnect) {
          BluetoothDevice(remoteId: d).connect(autoConnect: true, mtu: null).onError((e, s) {
            if (logLevel != LogLevel.none) {
              print("[FBP] [AutoConnect] connection failed: $e");
            }
          });
        }
      }
    }

    // keep track of connection states
    if (call.method == "OnConnectionStateChanged") {
      var r = BmConnectionStateResponse.fromMap(call.arguments);
      _connectionStates[r.remoteId] = r;
      if (r.connectionState == BmConnectionStateEnum.disconnected) {
        // push to mtu stream, if needed
        if (_mtuValues.containsKey(r.remoteId)) {
          var resp = BmMtuChangedResponse(remoteId: r.remoteId, mtu: 23);
          _methodStream.add(MethodCall("OnMtuChanged", resp.toMap()));
        }

        // clear mtu
        _mtuValues.remove(r.remoteId);

        // clear lastDescs (resets 'isNotifying')
        _lastDescs.remove(r.remoteId);

        // clear lastChrs (api consistency)
        _lastChrs.remove(r.remoteId);

        // cancel & delete subscriptions
        _deviceSubscriptions[r.remoteId]?.forEach((s) => s.cancel());
        _deviceSubscriptions.remove(r.remoteId);

        // Note: to make FBP easier to use, we do not clear `knownServices`,
        // otherwise `servicesList` would be more annoying to use. We also
        // do not clear `bondState`, for faster performance.

        // autoconnect
        if (Platform.isAndroid == false) {
          if (_autoConnect.contains(r.remoteId)) {
            if (_adapterStateNow == BmAdapterStateEnum.on) {
              var d = BluetoothDevice(remoteId: r.remoteId);
              d.connect(autoConnect: true, mtu: null).onError((e, s) {
                if (logLevel != LogLevel.none) {
                  print("[FBP] [AutoConnect] connection failed: $e");
                }
              });
            }
          }
        }
      }
    }

    // keep track of device name
    if (call.method == "OnNameChanged") {
      var device = BmNameChanged.fromMap(call.arguments);
      if (Platform.isMacOS || Platform.isIOS) {
        // iOS & macOS internally use the name changed callback for the platform name
        _platformNames[device.remoteId] = device.name;
      }
    }

    // keep track of services resets
    if (call.method == "OnServicesReset") {
      var r = BmBluetoothDevice.fromMap(call.arguments);
      _knownServices.remove(r.remoteId);
    }

    // keep track of bond state
    if (call.method == "OnBondStateChanged") {
      var r = BmBondStateResponse.fromMap(call.arguments);
      _bondStates[r.remoteId] = r;
    }

    // keep track of services
    if (call.method == "OnDiscoveredServices") {
      var r = BmDiscoverServicesResult.fromMap(call.arguments);
      if (r.success == true) {
        _knownServices[r.remoteId] = r;
      }
    }

    // keep track of mtu values
    if (call.method == "OnMtuChanged") {
      var r = BmMtuChangedResponse.fromMap(call.arguments);
      if (r.success == true) {
        _mtuValues[r.remoteId] = r;
      }
    }

    // keep track of characteristic values
    if (call.method == "OnCharacteristicReceived" || call.method == "OnCharacteristicWritten") {
      var r = BmCharacteristicData.fromMap(call.arguments);
      if (r.success == true) {
        _lastChrs[r.remoteId] ??= {};
        _lastChrs[r.remoteId]!["${r.serviceUuid}:${r.characteristicUuid}"] = r.value;
      }
    }

    // keep track of descriptor values
    if (call.method == "OnDescriptorRead" || call.method == "OnDescriptorWritten") {
      var r = BmDescriptorData.fromMap(call.arguments);
      if (r.success == true) {
        _lastDescs[r.remoteId] ??= {};
        _lastDescs[r.remoteId]!["${r.serviceUuid}:${r.characteristicUuid}:${r.descriptorUuid}"] = r.value;
      }
    }

    _methodStream.add(call);

    // cancel delayed subscriptions
    if (call.method == "OnConnectionStateChanged") {
      if (_delayedSubscriptions.isNotEmpty) {
        var r = BmConnectionStateResponse.fromMap(call.arguments);
        if (r.connectionState == BmConnectionStateEnum.disconnected) {
          var remoteId = r.remoteId;
          // use delayed to update the stream before we cancel it
          Future.delayed(Duration.zero).then((_) {
            _delayedSubscriptions[remoteId]?.forEach((s) => s.cancel()); // cancel
            _delayedSubscriptions.remove(remoteId); // delete
          });
        }
      }
    }
  }
相关推荐
大强同学2 小时前
UniGetUI:开源 GUI 包管理工具
windows·包管理
流氓也是种气质 _Cookie11 小时前
Wireshark在Windows XP系统上的安装与使用指南
windows·测试工具·wireshark
dshudsnb15 小时前
[ 2026最新 ] 在 Windows 11 上恢复已删除文件的 10 大方法
windows
xiaoshuaishuai818 小时前
C# 实现百度搜索算法逆向
开发语言·windows·c#·dubbo
ycjunhua19 小时前
windows 安装PostgreSQL 数据库
数据库·windows·postgresql
一个人旅程~20 小时前
Linux Mint(Ubuntu)如何在没有网卡驱动情况下利用手机上网安装旧电脑网卡驱动程序指导书
linux·windows·经验分享·电脑
格林威20 小时前
AI视觉检测:模型量化后漏检率上升怎么办?
人工智能·windows·深度学习·数码相机·计算机视觉·视觉检测·工业相机
无限进步_21 小时前
【C++】寻找字符串中第一个只出现一次的字符
开发语言·c++·ide·windows·git·github·visual studio
Ops菜鸟(Xu JieHao)1 天前
Linux 内网远程桌面Xrdp ~保姆级教程
linux·运维·服务器·windows·远程桌面·远程·xrdp