杰理 SDK 文档中心
首页
首页
  • 项目概览

    • 仓库简介与示例构成
    • 支持的平台与协议
  • 快速开始

    • 导入工程与编译运行
    • 蓝牙权限配置
  • 应用架构

    • 工程结构与模块分层
    • 核心类与回调接口
  • 核心功能

    • 蓝牙设备扫描
    • ATT 设备连接与断开管理
    • 数据收发与通知回调
  • 配置与调试

    • 协议配置常量
    • 日志系统与调试指南
  • 界面与交互

    • 设备扫描与连接界面
    • 设备详情与设置界面

ATT 设备连接与断开管理

ATTConnect 示例工程中的 BLE 连接管理能力,以 BleManager 单例为核心,封装了从扫描、配对、GATT 连接、MTU 协商、服务发现到主动断开、超时兜底与重连的完整生命周期管理。

Purpose and Scope

本文档介绍 ATTConnect 模块中 ATT 设备连接与断开管理 的实现机制,涵盖:

  • BleManager 单例的职责划分、初始化与销毁生命周期;
  • connectBleDevice() 连接流程(含 GATT over BR/EDR 的配对前置检查);
  • disconnectBleDevice() 断开流程与三种连接状态下的分支处理;
  • 基于主线程 Handler 的超时/兜底消息机制(连接、通知、MTU、服务发现、断开、配对超时);
  • 多设备连接状态管理(mBleDeviceMap、BleDevice 状态机、正在通讯设备切换)。

本页不涉及以下内容(由同级目录页覆盖):BLE 扫描与设备发现细节、数据透传与发送队列(SendBleDataThread)、蓝牙配对工具(BluetoothUtil.createBond)的底层实现。本文档聚焦"连接建立与拆除"这一条主线。

Overview

ATT(Attribute Protocol)是 BLE 链路层之上用于读写属性的协议。在 Android 上,应用通过 BluetoothGatt#connectGatt() 发起 GATT 连接,随后经历 onConnectionStateChange(连接状态变化)、MTU 协商(requestMtu)、服务发现(discoverServices)、特征通知使能(CCCD 写入)等阶段,才进入可通讯状态。

BleManager 将上述零散且异步的系统回调收敛为一个可感知连接状态、可超时兜底、可重连的封装层,向上层暴露简单的方法调用(connectBleDevice / disconnectBleDevice)与事件回调(BleEventCallback)。其设计意图包括:

  1. 集中管理连接生命周期:所有 GATT 对象、设备状态、超时定时器都挂在 BleManager 内部,避免上层 Activity 直接接触系统 API 的复杂性;
  2. 异步兜底:Android BLE 回调可能丢失或长时间不返回,因此为每个关键阶段都设置了超时消息(连接 40s、服务发现、MTU、断开 6s、配对 30s),超时后主动执行降级或断开策略;
  3. 支持多设备与 GATT over BR/EDR:ATTConnect 演示了传统 BLE(LE transport)与双模设备(BR/EDR transport)两种连接方式,后者要求先完成系统配对。

Architecture

flowchart TD
    subgraph sg_App["应用层 (ATTConnect)"]
        Activity["MainActivity / 业务页面"]
        CallbackImpl["BleEventCallback 实现"]
    end

    subgraph sg_BleManager["BleManager (单例)"]
        BM["BleManager instance"]
        Handler["主线程 Handler<br/>8 种超时/兜底消息"]
        CallbackMgr["BleEventCallbackManager<br/>回调分发"]
        DeviceMap["mBleDeviceMap<br/>Map<String, BleDevice>"]
        GattCallback["mBluetoothGattCallback"]
    end

    subgraph sg_Model["模型与工具"]
        BleDevice["BleDevice<br/>连接状态/MTU/Gatt"]
        ScanDeviceInfo["ScanDeviceInfo"]
        BluetoothUtil["BluetoothUtil<br/>createBond / deviceEquals"]
        Config["Config<br/>UUID 与 MTU 常量"]
    end

    subgraph sg_System["Android 系统层"]
        Adapter["BluetoothAdapter / LeScanner"]
        Gatt["BluetoothGatt"]
        SysReceiver["BroadcastReceiver<br/>蓝牙开关广播"]
    end

    Activity -->|"registerBleEventCallback"| BM
    BM -->|"onBleConnection 等事件"| CallbackMgr
    CallbackMgr -->|"分发"| CallbackImpl
    BM --> Handler
    Handler -->|"超时后强制断开/降级"| BM
    BM --> DeviceMap
    DeviceMap --> BleDevice
    BM --> GattCallback
    BM --> Config
    BM -->|"配对前置检查"| BluetoothUtil
    BM -->|"connectGatt / disconnect"| Gatt
    BM -->|"startScan / stopScan"| Adapter
    BM --> SysReceiver

架构说明:

  • BleManager 是全局唯一的门面(double-checked locking 单例,见 BleManager.java),持有 BluetoothAdapter、BluetoothLeScanner、设备 Map 与回调管理器;
  • 主线程 Handler 承担所有超时兜底逻辑(MSG_CONNECT_BLE_TIMEOUT 等 8 类消息),保证定时回调与 UI 线程一致,避免多线程竞态;
  • BleEventCallbackManager 负责把内部状态变化(连接、发现、扫描状态、连接设备列表变化)广播给一个或多个上层回调;
  • BleDevice 是每个远端设备的状态容器(连接状态、MTU、Gatt、发送线程),由 mBleDeviceMap 按地址索引;
  • BluetoothUtil 提供配对(createBond)、设备比较(deviceEquals)、系统已连接设备列表等系统级辅助能力,ATT/BR-EDR 连接前必须依赖它完成配对。

核心实现分析

BleManager 单例与初始化

BleManager 采用 volatile + 双重检查锁的单例模式,由 MyApplication 提供 Application 级 Context,因此整个进程生命周期内只有一个连接管理实例(BleManager.java):

private BleManager(Context context) {
    if (null == context) {
        throw new RuntimeException("Context can not be null.");
    }
    mContext = context;
    mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if (Build.VERSION.SDK_INT >= LOLLIPOP && mBluetoothAdapter != null) {
        mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
    }
    registerReceiver();
}

public static BleManager getInstance() {
    if (instance == null) {
        synchronized (BleManager.class) {
            if (instance == null) {
                instance = new BleManager(MyApplication.Companion.getApplication());
                JL_Log.w(TAG, "init", "instance : " + instance);
            }
        }
    }
    return instance;
}

Source: BleManager.java

设计意图:连接管理必须在整个 App 生命周期内唯一,任何页面(如设备列表页、通讯页)都能通过 getInstance() 拿到同一实例;registerReceiver() 在构造时注册蓝牙开关广播,蓝牙关闭时系统会强制断开 GATT,Manager 需据此同步内部状态。

核心常量(BleManager.java):

常量值含义
TRANSPORT_AUTO / TRANSPORT_BREDR / TRANSPORT_LE0 / 1 / 2对应 BluetoothDevice.connectGatt 的 transport 参数
BLE_UUID_SERVICE / BLE_UUID_WRITE / BLE_UUID_NOTIFICATION来自 Config业务服务/写/通知特征 UUID
BLE_UUID_NOTIFICATION_DESCRIPTOR00002902-0000-1000-8000-00805F9B34FBCCCD(客户端特征配置描述符),用于使能通知
SEND_DATA_MAX_TIMEOUT8000ms单次写数据最大超时
SCAN_BLE_TIMEOUT12s扫描超时
CONNECT_BLE_TIMEOUT40s连接超时
CALLBACK_TIMEOUT6000ms断开回调兜底超时
RECONNECT_BLE_DELAY2000ms重连延迟
BOUND_TIMEOUT30sATT/BR-EDR 配对超时

连接流程:connectBleDevice

connectBleDevice() 有三个重载,最终收敛到三参数版本 connectBleDevice(BluetoothDevice, int transport, int mtu)(BleManager.java):

public boolean connectBleDevice(BluetoothDevice device, int transport, int mtu) {
    if (!checkConnectEnv("connectBleDevice", device) || !isBluetoothEnable())
        return false;
    if (isConnectedDevice(device)) {
        mCallbackManager.onBleConnection(device, BluetoothProfile.STATE_CONNECTED);
        return true;
    }
    final BluetoothDevice connectingDevice = getConnectingDevice();
    if (connectingDevice != null) {
        JL_Log.e(TAG, "connectBleDevice", BluetoothUtil.formatString("Device(%s) is connecting, please wait.",
                printDeviceInfo(connectingDevice)));
        return isConnectingDevice(device);
    }
    if (isBleScanning()) {
        stopLeScan();
    }
    //回调BLE连接中状态
    final BleDevice bleDevice = addBleDevice(device);
    bleDevice.setTransport(transport).setRequestMtu(mtu);
    startConnectTimeout(device);
    handleBleConnection(device, BluetoothProfile.STATE_CONNECTING);

    if (bleDevice.isATTDevice()) { //连接Gatt over BR/EDR, 需要判断是否已配对
        final int boundState = device.getBondState();
        if (boundState != BluetoothDevice.BOND_BONDED) {
            boolean ret = boundState == BluetoothDevice.BOND_BONDING; //正在配对中,等待配对结果
            mHandler.removeMessages(MSG_BOUND_DEVICE_TIMEOUT);
            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_BOUND_DEVICE_TIMEOUT, device), BOUND_TIMEOUT);
            if (!ret) {
                //未配对,进行配对流程
                ret = BluetoothUtil.createBond(mContext, device);
                ...
                if (!ret) {
                    mHandler.removeMessages(MSG_BOUND_DEVICE_TIMEOUT);
                    handleBleConnection(device, BluetoothProfile.STATE_DISCONNECTED);
                }
            }
            return ret;
        }
    }

    BluetoothGatt gatt = null;
    try {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
            gatt = device.connectGatt(mContext, false, mBluetoothGattCallback,
                    transport, 1, mHandler);
        } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O) {
            gatt = device.connectGatt(mContext, false, mBluetoothGattCallback,
                    transport, 1);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            gatt = device.connectGatt(mContext, false, mBluetoothGattCallback, transport);
        } else {
            gatt = device.connectGatt(mContext, false, mBluetoothGattCallback);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    boolean ret = gatt != null;
    bleDevice.setGatt(gatt);
    ...
    if (!ret) {
        handleBleConnection(device, BluetoothProfile.STATE_DISCONNECTED);
    }
    return ret;
}

Source: BleManager.java

连接流程的关键设计点:

  1. 幂等保护:若设备已在连接列表中且状态为 STATE_CONNECTED,直接补发一次 STATE_CONNECTED 回调并返回 true,避免重复建连;若已有其他设备处于 connecting,则拒绝新连接并返回 isConnectingDevice(device) 的结果——同一时间只允许一个设备处于连接中。
  2. 扫描互斥:发起连接前若正在扫描则先 stopLeScan(),减少扫描与连接的射频冲突。
  3. 状态先行:addBleDevice() 创建 BleDevice 并登记到 mBleDeviceMap,随后立即回调 STATE_CONNECTING,让 UI 第一时间进入"连接中"展示。
  4. ATT/BR-EDR 配对前置:BleDevice.isATTDevice() 判定为双模 ATT 设备时,连接前必须完成系统配对。未配对则调用 BluetoothUtil.createBond() 发起配对,并启动 30s 的 MSG_BOUND_DEVICE_TIMEOUT 兜底;配对失败直接回调 STATE_DISCONNECTED。
  5. 版本化 connectGatt:按 SDK 版本选择不同的 connectGatt 重载——Android 8.1+ 使用带 transport, phy=1, Handler 的 6 参数版本(phy 固定为 1,即 PHY_LE_1M);Android 8.0 使用 5 参数版本;Android 6.0+ 使用带 transport 的版本;旧版本退回无 transport 版本。autoConnect=false(第二个参数),即发起主动连接而非后台监听。
  6. 失败兜底:connectGatt 抛异常或返回 null 时回调 STATE_DISCONNECTED,保证状态机不悬挂。

默认参数重载(BleManager.java):

public boolean connectBleDevice(BluetoothDevice device, int transport) {
    return connectBleDevice(device, transport, Config.REQUEST_BLE_MTU);
}

public boolean connectBleDevice(BluetoothDevice device) {
    return connectBleDevice(device, TRANSPORT_LE);
}

Source: BleManager.java

即上层最常见的 connectBleDevice(device) 等价于"LE transport + 默认请求 MTU";请求 MTU 的值来自 Config.REQUEST_BLE_MTU,传统 BLE 连接成功后 Manager 会执行 MTU 协商(Android 建议范围 20~509)。

断开流程:disconnectBleDevice

disconnectBleDevice() 按 BleDevice 当前连接状态分三条路径处理(BleManager.java):

public void disconnectBleDevice(BluetoothDevice device) {
    if (!checkConnectEnv("disconnectBleDevice", device)) return;
    BleDevice bleDevice = getBleDevice(device);
    ...
    final BluetoothGatt gatt = bleDevice.getGatt();
    if (!isBluetoothEnable()) {
        bleDevice.setConnection(BluetoothProfile.STATE_DISCONNECTED);
    }
    switch (bleDevice.getConnection()) {
        case BluetoothProfile.STATE_CONNECTED: {
            if (null != gatt) {
                mHandler.removeMessages(MSG_DISCONNECT_BLE_TIMEOUT);
                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_DISCONNECT_BLE_TIMEOUT, device), CALLBACK_TIMEOUT);
                gatt.disconnect();
            }
            break;
        }
        case BluetoothProfile.STATE_CONNECTING: {
            if (null != gatt) {
                gatt.disconnect();
                closeGatt(gatt);
            }
            handleBleConnection(device, BluetoothProfile.STATE_DISCONNECTED);
            break;
        }
        default:
            removeConnectedBle(device);
            mCallbackManager.onBleConnection(device, BluetoothProfile.STATE_DISCONNECTED);
            break;
    }
}

Source: BleManager.java

三条路径的设计意图:

  • STATE_CONNECTED(已连接):调用 gatt.disconnect() 走系统正常断开流程,期望 onConnectionStateChange(STATE_DISCONNECTED) 回调驱动后续清理;同时启动 6s 的 MSG_DISCONNECT_BLE_TIMEOUT 兜底——若系统回调迟迟不来(某些厂商 ROM 的已知问题),由 Handler 直接 closeGatt() 并强制回调 STATE_DISCONNECTED(见 BleManager.java)。
  • STATE_CONNECTING(连接中):此时没有可靠的系统回调,直接 disconnect + close 并同步回调 STATE_DISCONNECTED,让 UI 立即恢复。
  • 其他状态(含已断开/未知):仅从 Map 中移除并补发断开回调,保证事件对称。
  • 另外,如果蓝牙本身已关闭(isBluetoothEnable() == false),先把 BleDevice 状态强制置为 STATE_DISCONNECTED,因为系统不会再有回调。

超时兜底消息机制

整个连接生命周期由主线程 Handler 驱动超时控制,共 8 种消息(BleManager.java):

消息触发时机超时值兜底动作
MSG_SCAN_BLE_TIMEOUT (0x1010)startLeScan12sstopLeScan()
MSG_CONNECT_BLE_TIMEOUT (0x1011)connectBleDevice40sdisconnectBleDevice(device)
MSG_SCAN_HID_DEVICE (0x1012)周期任务1s 循环同步系统已配对的 BLE 设备
MSG_NOTIFY_BLE_TIMEOUT (0x1013)使能通知6sdisconnectBleDevice(device)
MSG_CHANGE_BLE_MTU_TIMEOUT (0x1014)MTU 协商—MTU 降级为 20,继续按已连接处理
MSG_BLE_DISCOVER_SERVICES_CALLBACK_TIMEOUT (0x1015)服务发现—已有服务则补发 onServicesDiscovered,否则置 needReconnect=true 并断开
MSG_DISCONNECT_BLE_TIMEOUT (0x1016)disconnectBleDevice6scloseGatt() + 强制回调 STATE_DISCONNECTED
MSG_BOUND_DEVICE_TIMEOUT (0x1017)ATT 设备配对30s回调 STATE_DISCONNECTED

例如 MTU 协商超时时(BleManager.java):

case MSG_CHANGE_BLE_MTU_TIMEOUT: {
    if (!(msg.obj instanceof BluetoothDevice)) return false;
    BluetoothDevice device = (BluetoothDevice) msg.obj;
    BleDevice bleDevice = getBleDevice(device);
    ...
    if (null == bleDevice) return false;
    bleDevice.setMtu(20);
    handleBleConnection(device, BluetoothProfile.STATE_CONNECTED);
    break;
}

Source: BleManager.java

设计意图:Android 的 GATT 回调是异步且不保证可靠的,超时兜底把"等系统回调"变成"自己掌握进度"。MTU 协商失败不视为连接失败——降级到 20 字节默认 MTU 继续通讯,这是对低质量外设的宽容处理;而服务发现超时若没有任何服务缓存,则标记 needReconnect 并断开,为后续自动重连(RECONNECT_BLE_DELAY = 2000ms)创造条件。

Core Flow

连接建立序列

sequenceDiagram
    participant App as 上层业务
    participant BM as BleManager
    participant H as 主线程 Handler
    participant U as BluetoothUtil
    participant G as BluetoothGatt
    participant CB as mBluetoothGattCallback

    App->>BM: connectBleDevice(device, transport, mtu)
    BM->>BM: 幂等检查(已连接/连接中/扫描互斥)
    BM->>BM: addBleDevice + 回调 STATE_CONNECTING
    BM->>H: 发送 MSG_CONNECT_BLE_TIMEOUT (40s)
    alt ATT 设备且未配对
        BM->>U: createBond(device)
        U-->>BM: 配对结果
        BM->>H: MSG_BOUND_DEVICE_TIMEOUT (30s)
    end
    BM->>G: connectGatt(context, false, CB, transport, phy=1, handler)
    G-->>CB: onConnectionStateChange(CONNECTED)
    CB->>BM: handleBleConnection(device, STATE_CONNECTED)
    BM->>H: 移除连接超时消息
    BM->>G: requestMtu(Config.REQUEST_BLE_MTU)
    G-->>CB: onMtuChanged(mtu)
    CB->>BM: 更新 BleDevice.mtu
    BM->>G: discoverServices()
    G-->>CB: onServicesDiscovered
    CB->>BM: 使能通知(写 CCCD 00002902)
    BM-->>App: 回调 STATE_CONNECTED / 服务就绪

断开序列

sequenceDiagram
    participant App as 上层业务
    participant BM as BleManager
    participant H as 主线程 Handler
    participant G as BluetoothGatt
    participant CB as mBluetoothGattCallback

    App->>BM: disconnectBleDevice(device)
    alt 已连接
        BM->>H: MSG_DISCONNECT_BLE_TIMEOUT (6s 兜底)
        BM->>G: gatt.disconnect()
        G-->>CB: onConnectionStateChange(DISCONNECTED)
        CB->>BM: handleBleConnection(device, STATE_DISCONNECTED)
    else 连接中
        BM->>G: disconnect + closeGatt
        BM-->>App: 回调 STATE_DISCONNECTED
    else 其他状态
        BM->>BM: removeConnectedBle(device)
        BM-->>App: 回调 STATE_DISCONNECTED
    end
    H-->>BM: MSG_DISCONNECT_BLE_TIMEOUT 到达(若系统回调未返回)
    BM->>G: closeGatt + 强制回调 STATE_DISCONNECTED

连接状态与多设备管理

状态机

BleDevice 内部以 BluetoothProfile.STATE_* 常量表达连接状态,BleManager 通过 handleBleConnection() 驱动状态迁移(未展示其完整实现,但其在连接超时、MTU 超时、断开兜底等消息中被反复调用):

stateDiagram-v2
    [*] --> IDLE: 扫描发现/系统同步
    IDLE --> CONNECTING: connectBleDevice
    CONNECTING --> CONNECTED: onConnectionStateChange(CONNECTED) / MTU 协商完成
    CONNECTING --> IDLE: 连接失败 / 配对失败 / 连接超时(40s)
    CONNECTED --> CONNECTED: 服务发现超时(needReconnect=true)
    CONNECTED --> IDLE: disconnectBleDevice / 通知超时(6s) / 蓝牙关闭
    IDLE --> CONNECTING: 自动重连 (延迟2s)

状态迁移的约束:同一时刻最多一个设备处于 CONNECTING(getConnectingDevice() 遍历 Map 查找 bleDevice.isConnecting());mUsingDevice(正在通讯设备)在 removeConnectedBle() 中被维护——当连接列表清空时置 null,否则切换到按连接时间倒序排序后的第一个设备(BleManager.java):

private BleDevice removeConnectedBle(String address) {
    if (!BluetoothAdapter.checkBluetoothAddress(address)) return null;
    BleDevice bleDevice = mBleDeviceMap.remove(address);
    ...
    if (null == bleDevice) return null;
    bleDevice.stopSendDataThread();
    final List<BluetoothDevice> connectedDeviceList = getConnectedDeviceList();
    ...
    if (connectedDeviceList.isEmpty()) {
        setConnectedBtDevice(null);
    } else if (BluetoothUtil.deviceEquals(bleDevice.getDevice(), getConnectedBtDevice())) {
        setConnectedBtDevice(connectedDeviceList.get(0));
    }
    return bleDevice;
}

Source: BleManager.java

设备数据模型

classDiagram
    class BleManager {
        -mBleDeviceMap Map~String, BleDevice~
        -mUsingDevice BluetoothDevice
        -mDiscoveredBleDevices List~BluetoothDevice~
        -mCallbackManager BleEventCallbackManager
        -mBluetoothGattCallback BluetoothGattCallback
        +connectBleDevice(device, transport, mtu) boolean
        +disconnectBleDevice(device) void
        +getConnectedDeviceList() List~BluetoothDevice~
        +isConnectedDevice(address) boolean
    }

    class BleDevice {
        -device BluetoothDevice
        -gatt BluetoothGatt
        -connection int
        -mtu int
        -requestMtu int
        -transport int
        -needReconnect boolean
        -sendDataThread SendBleDataThread
        +isATTDevice() boolean
        +isConnecting() boolean
        +setConnection(state) void
    }

    class BleEventCallbackManager {
        +registerBleEventCallback(cb) void
        +unregisterBleEventCallback(cb) void
        +onBleConnection(device, state) void
        +onDiscoveryState(scanning) void
        +release() void
    }

    class IBleOp {
        <<interface>>
        +getBleMtu() int
        +writeDataByBle(gatt, serviceUUID, characteristicUUID, data) boolean
    }

    BleManager "1" *-- "many" BleDevice : 管理
    BleManager "1" *-- "1" BleEventCallbackManager : 持有
    BleEventCallbackManager --> BleEventCallback : 分发
    BleManager ..|> IBleOp : 实现(如适用)

IBleOp 是数据通讯操作的抽象接口(IBleOp.java):

public interface IBleOp {
    int getBleMtu();
    boolean writeDataByBle(BluetoothGatt gatt, UUID serviceUUID, UUID characteristicUUID, byte[] data);
}

Source: IBleOp.java

Usage Examples

1. 注册连接事件回调

上层页面通过 registerBleEventCallback 订阅连接事件,在 onBleConnection 中根据 BluetoothProfile.STATE_* 刷新 UI(BleManager.java):

public void registerBleEventCallback(BleEventCallback callback) {
    mCallbackManager.registerBleEventCallback(callback);
}

public void unregisterBleEventCallback(BleEventCallback callback) {
    mCallbackManager.unregisterBleEventCallback(callback);
}

Source: BleManager.java

回调在页面 onDestroy 时必须注销,避免泄漏;BleEventCallbackManager.release() 会在 BleManager.destroy() 时清理全部回调。

2. 发起连接

传统 BLE 设备(LE transport)与 ATT/双模设备(BR/EDR transport)使用同一入口,区别只在 transport 参数(BleManager.java):

// 传统 BLE:默认 LE transport + Config.REQUEST_BLE_MTU
bleManager.connectBleDevice(device);

// 显式指定 transport 与 MTU(如双模 ATT 设备走 BR/EDR)
bleManager.connectBleDevice(device, BleManager.TRANSPORT_BREDR, 247);

Source: BleManager.java

注意:使用 TRANSPORT_BREDR 时设备必须是双模设备且已配对(connectBleDevice 内部会自动触发 createBond);BR/EDR 通道不支持 MTU 调整。

3. 主动断开与状态查询

// 主动断开:已连接状态走 gatt.disconnect() + 6s 兜底;连接中状态立即 close
bleManager.disconnectBleDevice(device);

// 状态查询:判断设备是否在已连接列表中
boolean connected = bleManager.isConnectedDevice(device.getAddress());

// 获取按连接时间倒序的已连接设备列表
List<BluetoothDevice> devices = bleManager.getConnectedDeviceList();

Source: BleManager.java

4. 资源释放

BleManager.destroy() 在 App 退出时调用:注销广播、停止连接超时、断开并清空所有设备、停止扫描、释放回调管理器与 Handler 消息(BleManager.java):

public void destroy() {
    JL_Log.w(TAG, "destroy", "instance : " + instance);
    unregisterReceiver();
    stopConnectTimeout();
    clearConnectedBleDevices();
    if (isBleScanning()) stopLeScan();
    isBleScanning(false);
    mDiscoveredBleDevices.clear();
    mCallbackManager.release();
    mHandler.removeCallbacksAndMessages(null);
    instance = null;
}

Source: BleManager.java

Configuration Options

连接管理相关的可配置项集中在 Config(com.jieli.bt.att.data.constant.Config)与 BleManager 内部常量中:

配置项类型默认值说明
Config.getBLE_SERVICE_UUID()UUID业务定义BLE 服务 UUID,连接后服务发现的筛选基准
Config.getBLE_WRITE_UUID()UUID业务定义写特征 UUID(BLE_UUID_WRITE)
Config.getBLE_NOTIFY_UUID()UUID业务定义通知特征 UUID(BLE_UUID_NOTIFICATION)
Config.REQUEST_BLE_MTUint业务定义连接成功后请求的 MTU,范围 [20, 509]
BleManager.TRANSPORT_AUTO/BREDR/LEint0/1/2connectGatt 的传输层选择
CONNECT_BLE_TIMEOUTint40s连接阶段最大等待时间,超时自动断开
CALLBACK_TIMEOUTint6sgatt.disconnect() 后等待系统回调的兜底时间
BOUND_TIMEOUTint30sATT 设备配对等待上限
SCAN_BLE_TIMEOUTint12s扫描自动停止时间
RECONNECT_BLE_DELAYint2s服务发现失败后的自动重连延迟
BLE_UUID_NOTIFICATION_DESCRIPTORUUID00002902-...-00805F9B34FBCCCD 描述符 UUID,通知使能写入目标

运行时开关(无 UI 配置项,由代码路径控制):

  • 是否启用自动重连:由 BleDevice.needReconnect 标记,服务发现超时且无服务缓存时置位;
  • 是否允许并发连接:不允许——connectBleDevice 在已有 connecting 设备时拒绝新连接;
  • 蓝牙权限:checkScanEnv 要求扫描权限 + 定位权限;checkConnectEnv 要求连接权限(BleManager.java)。

API Reference

以下为 BleManager 中与连接/断开管理直接相关的公开 API(签名以源码为准)。

getInstance(): BleManager

返回全局单例。首次调用时以 MyApplication 的 Context 初始化,注册蓝牙开关广播。

Throws: RuntimeException — 传入 null Context 时(仅构造路径)。

connectBleDevice(BluetoothDevice device, int transport, int mtu): boolean

发起 GATT 连接的主入口(含配对前置、超时启动、STATE_CONNECTING 回调)。

参数:

  • device (BluetoothDevice): 目标设备,由扫描或系统同步获得
  • transport (int): TRANSPORT_AUTO / TRANSPORT_BREDR / TRANSPORT_LE
  • mtu (int): 请求的 MTU 值,范围 [20, 509](BR/EDR 不支持)

返回: true 表示连接流程已启动(或设备已连接);false 表示权限缺失、设备为空、蓝牙关闭、connectGatt 失败。

重载: connectBleDevice(device, transport) 使用 Config.REQUEST_BLE_MTU;connectBleDevice(device) 等价于 LE transport。

disconnectBleDevice(BluetoothDevice device): void

按状态分支执行断开:已连接 → gatt.disconnect() + 6s 兜底;连接中 → 立即 close 并回调断开;其他 → 移除并补发回调。

isConnectedDevice(BluetoothDevice device) / isConnectedDevice(String address): boolean

判断设备是否处于已连接列表(遍历 getConnectedDeviceList() 按地址比对)。

getConnectedDeviceList(): List<BluetoothDevice>

返回按连接时间倒序的已连接设备列表;内部过滤 connection != STATE_CONNECTED 或无 Gatt 的条目。

getConnectedBleDeviceList(Context context): List<BluetoothDevice>(静态)

通过 BluetoothManager.getConnectedDevices(BluetoothProfile.GATT) 查询系统级已连接设备,需要连接权限。

getConnectedBtDevice(): BluetoothDevice

返回当前正在通讯的设备 mUsingDevice(唯一活动通讯对象)。

getConnectedBtGatt(BluetoothDevice device): BluetoothGatt

从 mBleDeviceMap 取出对应 BleDevice 并返回其 Gatt;未登记返回 null。

getBleMtu(BluetoothDevice device): int

返回设备当前协商后的 MTU(未连接返回 0)。

isConnecting(): boolean / isConnectingDevice(BluetoothDevice device): boolean

是否存在处于连接中的设备;以及指定设备是否正是连接中的那一个(BluetoothUtil.deviceEquals 比较)。

registerBleEventCallback(BleEventCallback callback) / unregisterBleEventCallback(...)

注册/注销连接事件回调;destroy() 时由 BleEventCallbackManager.release() 统一清理。

startLeScan(long timeout): boolean / stopLeScan(): boolean

扫描控制。连接管理相关行为:connectBleDevice 在扫描中会自动停止扫描;扫描超时消息 MSG_SCAN_BLE_TIMEOUT(12s 默认)自动停止。

writeDataByBleAsync(BluetoothDevice device, UUID serviceUUID, UUID characteristicUUID, byte[] data, OnWriteDataCallback callback): void

异步写数据入口(数据发送队列属于"数据透传"页范围,此处仅列出连接层暴露的入口)。

destroy(): void

释放全部连接资源并将单例置空。

Failure Modes, Edge Cases & Concurrency

故障模式与降级策略

故障场景检测方式系统行为设计意图
连接 40s 无回调MSG_CONNECT_BLE_TIMEOUT自动 disconnectBleDevice系统回调丢失时不让 UI 永久停留在"连接中"
connectGatt 返回 null / 抛异常返回值检查 + try-catch回调 STATE_DISCONNECTED资源异常立即暴露,避免悬挂
断开后系统回调 6s 未达MSG_DISCONNECT_BLE_TIMEOUTcloseGatt() + 强制回调断开部分 ROM 的 onConnectionStateChange 缺失问题
MTU 协商超时/失败MSG_CHANGE_BLE_MTU_TIMEOUTMTU 降级为 20,仍按已连接处理宽容处理,不因 MTU 失败中断连接
服务发现超时且无服务MSG_BLE_DISCOVER_SERVICES_CALLBACK_TIMEOUTneedReconnect=true + 断开(2s 后重连)有缓存服务可补发回调;无缓存则重连恢复
ATT 设备未配对getBondState() 检查createBond() + 30s 配对超时BR/EDR 连接必须前置配对;正在配对时等待结果
配对失败createBond 返回 false移除超时消息 + 回调断开失败路径即时收敛
蓝牙被用户关闭蓝牙开关广播 + isBluetoothEnable()强制置 STATE_DISCONNECTED,断开路径不再依赖系统回调系统在蓝牙关闭时会断开所有 GATT,必须同步内部状态
通知使能超时MSG_NOTIFY_BLE_TIMEOUT断开设备无法收数据时连接无意义,宁可重连
重复连接同一已连接设备isConnectedDevice补发 STATE_CONNECTED 回调并返回 true幂等,避免重复建连导致多 Gatt 实例

边界情况

  • 空设备:connectBleDevice / disconnectBleDevice 均先经 checkConnectEnv 拒绝 null 设备(BleManager.java);
  • 非法地址:getConnectedBLEDevice / removeConnectedBle 使用 BluetoothAdapter.checkBluetoothAddress 校验地址格式;
  • 已连接列表为空:removeConnectedBle 将 mUsingDevice 置 null;否则切换到排序后第一个设备,保证"正在通讯设备"指针始终有效;
  • 扫描与连接互斥:连接前停止扫描;扫描中调用 startLeScan 会 flush 并重置超时,防止重复扫描器;
  • 多设备连接:同一时刻仅允许一个 connecting 设备;但已连接设备允许多个(Map 按地址存储),mUsingDevice 指向当前活动通讯设备。

并发与一致性

  • 单例字段(instance、mUsingDevice、isBleScanning)使用 volatile 保证可见性;
  • 所有超时消息与回调分发都发生在主线程 Handler(Looper.getMainLooper())上,onConnectionStateChange 等 GATT 回调本质也在主线程派发(Android 默认),因此状态读写天然串行化,无需额外加锁;
  • mBleDeviceMap / mDiscoveredBleDevices 为普通 HashMap / ArrayList,仅在主线程路径访问;clearConnectedBleDevices() 通过克隆 Map 迭代,避免 disconnectBleDevice 中 removeConnectedBle 修改集合导致 ConcurrentModificationException;
  • destroy() 与业务页面生命周期交错时,instance=null 后新页面调用 getInstance() 会重建实例——旧 Handler 消息已通过 removeCallbacksAndMessages(null) 清空,避免悬挂消息操作已销毁状态。

Performance & Operational Considerations

  • 扫描参数:SCAN_MODE_LOW_LATENCY + MATCH_MODE_AGGRESSIVE + MATCH_NUM_MAX_ADVERTISEMENT,Android 8.0+ 使用 PHY_LE_ALL_SUPPORTED,以最快速度发现设备,代价是功耗较高,因此默认 12s 自动停止(BleManager.java);
  • 超时聚合:startConnectTimeout 用 hasMessages 防止重复投递 40s 连接超时,避免 Handler 队列堆积;
  • 资源回收:disconnectBleDevice 已连接路径只 disconnect() 不 close(),把 Gatt 关闭交给系统回调后的清理逻辑;连接中路径才立即 closeGatt()——这平衡了"复用连接"与"及时释放";
  • 日志:全程使用 JL_Log 输出设备地址、状态与关键决策点,线上定位连接问题时以 BleManager 的 TAG 日志为主线排查;
  • 权限前置:扫描需要定位权限,连接需要 BLUETOOTH_CONNECT 权限,所有入口先做权限检查再执行系统调用,避免系统抛 SecurityException。

Extension Points

  • BleEventCallback / IBleEventCallback:连接状态、扫描状态、设备列表变化的统一事件出口。新增业务(如弹窗提示断连、自动重连策略)通过实现该接口并注册即可,无需改动 BleManager 内部逻辑;
  • IBleOp:数据读写能力的抽象接口(getBleMtu / writeDataByBle),便于替换底层 BLE 实现或注入测试替身;
  • transport 参数:connectBleDevice 的 transport 入参天然支持未来新增的双模/经典设备接入方式;
  • BleDevice.isATTDevice():设备类型判定点,扩展新设备类型(如 HID over GATT)时在此扩展类型路由逻辑;
  • Config 常量:服务/写/通知 UUID 与请求 MTU 集中在 Config,适配不同固件协议时只需调整配置,无需修改 Manager。

Related Links

  • BleManager.java — 本文核心:连接/断开/超时/多设备管理
  • IBleOp.java — 数据读写抽象接口
  • BleEventCallbackManager.java — 回调注册与分发
  • BleDevice.java — 设备状态容器(连接状态/MTU/发送线程)
  • BluetoothUtil.java — 配对、设备比较、系统设备同步工具
  • 同级目录:4.2-device-connection 下的 BLE 扫描与发现页、数据透传页(SendBleDataThread)分别覆盖扫描与通讯细节
Prev
蓝牙设备扫描
Next
数据收发与通知回调