# 蓝牙 BLE / GATT

> 取代 JSR-82/SPP 的异步 BLE/GATT 栈（BluetoothManager）。

Source: https://crossfly.dev/zh/docs/data-and-services/device-bluetooth-ble — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# 蓝牙 BLE / GATT

CrossFly 使用一个**异步 BLE/GATT 栈**（`dev.crossfly.device.bluetooth`），它**取代**了
旧的 JSR-82/SPP。其建模基于 `CompletableFuture`。

## ⚠️ 坦诚提示（写代码前请阅读）
在 **core** 中，若干操作是**基于内存状态的骨架** —— `BluetoothConnectionBuilder.build()`
创建空的流，`GattCharacteristic.read()/write()` 只操作本地缓存，而 `discoverServices()`
返回空列表。**真正的无线电传输**来自平台启动器（Android/iOS）。而
**建模**（UUID、future、service→characteristic→descriptor 层级）已是最终形态；可依赖该
API，但要在每个平台的真实设备上测试。

## 发现与连接
```java
BluetoothManager bt = BluetoothManager.getInstance();   // throws IOException

// 扫描（异步）
bt.scanForDevices(10, TimeUnit.SECONDS, new BluetoothManager.ScanCallback() {
   public void onSuccess(List<BluetoothDevice> devices) { /* 选择一个 */ }
   public void onError(String error) { Debug.log(error); }
});

// 连接（异步）— 返回一个 BluetoothConnection
bt.connect(device, new BluetoothManager.ConnectionCallback() {
   public void onSuccess(BluetoothConnection conn) { usar(conn); }
   public void onError(String error) { Debug.log(error); }
});
```
存在 **`*Sync`** 变体（`scanForDevicesSync`、`connectSync`）—— **仅在 worker 线程中使用**，绝不
在绘制线程上使用。

## GATT —— 读取电量并订阅通知
**service → characteristic → descriptor** 模型，通过 `CompletableFuture` 进行 I/O：
```java
device.discoverServices(services -> {
   GattService bateria = device.getService("0000180F-0000-1000-8000-00805F9B34FB");
   GattCharacteristic nivel = bateria.getCharacteristic("00002A19-0000-1000-8000-00805F9B34FB");

   nivel.read().thenAccept(bytes -> {
      int pct = bytes[0] & 0xFF;
      Component.onUI(() -> label.setText(pct + "%"));
   });

   nivel.enableNotifications(bytes ->                     // 订阅变更
      Component.onUI(() -> label.setText((bytes[0] & 0xFF) + "%")));
});
```
`GattCharacteristic` 暴露 `read()` → `CompletableFuture<byte[]>`、`write(byte[])` → `CompletableFuture<Void>`、
`enableNotifications(Consumer<byte[]>)`，以及能力检测 `canRead()`/`canWrite()`/`canNotify()`。

## `BluetoothConnection` 是 `AutoCloseable`
```java
try (BluetoothConnection conn = bt.connectSync(device)) {   // 仅在 worker thread 中
   conn.write("PING".getBytes());
   byte[] resp = conn.read();
}   // 自动关闭
```

## 最佳实践
- **在 UI 中用异步**（`connect(...)`），**`*Sync` 仅在 worker 中**。
- 回调/future 用 `Component.onUI(...)` 返回 UI。
- 对 `BluetoothConnection` 使用 `try-with-resources`。
- 在每个平台的**真实设备上测试**（core 只是建模；无线电存在于启动器中）。

## 常见错误
- 在绘制线程上调用 `*Sync` 变体（阻塞 UI）。
- 期望 **core** 执行真正的无线电 I/O（它只是骨架；传输属于平台）。
- 在 `thenAccept`/回调中直接操作 UI 而不用 `onUI(...)`。

## 另见
- [声音与设备工具](/docs/data-and-services/device-sound-and-misc) · [各平台权限](/docs/platforms)
