3、经典蓝牙(BR/EDR)基础:蓝牙适配器(BluetoothAdapter)操作、开启/关闭蓝牙、搜索设备、绑定配对流程

好,咱们正式开始动手操作蓝牙了。

这一章,我带你搞定经典蓝牙最核心的几个操作:蓝牙适配器开关蓝牙搜索设备绑定配对。说白了,就是让手机能发现别人,也能被别人发现。

3.1 蓝牙适配器(BluetoothAdapter)—— 一切操作的入口

在 Android 里,所有蓝牙操作都绕不开一个对象:BluetoothAdapter。你可以把它理解成「蓝牙硬件的大门」。没有它,你什么都干不了。

获取它的方式很简单:

BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

如果返回 null,说明设备不支持蓝牙。嗯,这种情况现在很少见了,但我记得早期在一些平板上遇到过。

个人习惯:我每次获取 BluetoothAdapter 后,都会先判空。别嫌麻烦,线上 crash 往往就出在这种「想当然」的地方。

3.2 开启/关闭蓝牙

拿到适配器后,第一件事就是检查蓝牙状态。

if (!bluetoothAdapter.isEnabled()) {
    // 蓝牙未开启
}

开启蓝牙有两种方式:

  1. 隐式启动(推荐):通过 Intent 让用户确认
  2. 直接启动:调用 enable() 方法(需要系统权限)

我一般用第一种,用户体验更好:

Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);

为什么不用 enable()

因为 enable() 是静默开启,用户完全不知道。我曾经在一个项目里这么干过,结果被用户投诉「手机偷偷开蓝牙,是不是在偷我数据?」—— 从那以后,我再也不敢用直接开启了。

注意:直接调用 bluetoothAdapter.enable() 需要 BLUETOOTH_ADMIN 权限,且 Android 10 以上限制更严格。非系统应用不建议使用。

关闭蓝牙更简单:

bluetoothAdapter.disable();

但同样需要 BLUETOOTH_ADMIN 权限。我个人建议:关闭操作也交给用户自己决定,别替用户做主。

3.3 搜索设备 —— 让蓝牙「看见」周围

蓝牙搜索,说白了就是让手机发出询问信号,周围设备回应「我在这里」。

开始搜索:

bluetoothAdapter.startDiscovery();

停止搜索:

bluetoothAdapter.cancelDiscovery();

搜索结果通过广播接收器获取。你需要注册监听 BluetoothDevice.ACTION_FOUND

BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            String name = device.getName();
            String address = device.getAddress();
            // 处理发现的设备
        }
    }
};

这里有个坑,我踩过好几次:

搜索期间不能同时进行连接操作。 蓝牙搜索非常耗资源,如果你在搜索时尝试连接,大概率会失败。我的做法是:先停止搜索,再发起连接。

另外,搜索时间一般持续 12 秒左右。你可以通过 ACTION_DISCOVERY_FINISHED 监听搜索结束。

3.4 绑定配对流程 —— 建立信任关系

搜索到设备后,下一步就是配对。配对说白了就是交换密钥,建立信任关系。

配对流程分两步:

  1. 发起配对请求
  2. 用户确认 PIN 码或配对码

代码层面,你只需要调用:

device.createBond();

系统会自动弹出配对对话框。但这里有个细节:createBond() 是隐藏 API,需要通过反射调用:

Method createBondMethod = device.getClass().getMethod("createBond");
createBondMethod.invoke(device);

或者,更稳妥的方式是监听 ACTION_PAIRING_REQUEST 广播,手动处理配对:

IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST);
registerReceiver(pairingReceiver, filter);

在广播接收器里,你可以设置 PIN 码或者确认配对:

if (BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action)) {
    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
    // 自动配对(比如 PIN 码固定为 "1234")
    device.setPin("1234".getBytes());
    // 或者直接确认配对
    device.setPairingConfirmation(true);
}
避坑指南:我曾经在一个物联网项目里,需要设备自动配对,不弹用户对话框。结果发现不同厂商的蓝牙芯片对配对流程支持不一样。有的需要先 setPinsetPairingConfirmation,有的顺序反了就会失败。我的建议是:先查一下目标设备的蓝牙协议栈文档,别想当然。

3.5 获取已绑定设备列表

有时候你不需要重新搜索,直接拿之前配过对的设备就行:

Set<BluetoothDevice> bondedDevices = bluetoothAdapter.getBondedDevices();
for (BluetoothDevice device : bondedDevices) {
    Log.d("Bluetooth", "已绑定设备:" + device.getName() + " - " + device.getAddress());
}

这个方法返回的是已经完成配对的设备。注意,配对不等于连接。配对只是交换了密钥,连接才是真正的数据传输通道。

3.6 权限声明(Android 12+ 变化)

最后,别忘了权限。Android 12(API 31)开始,蓝牙权限有了重大变化:

权限 作用 Android 12+
BLUETOOTH 基本蓝牙操作(连接、通信) 需要运行时申请
BLUETOOTH_ADMIN 管理蓝牙(搜索、配对) 需要运行时申请
ACCESS_FINE_LOCATION 传统蓝牙搜索需要位置权限 Android 12 以下需要
BLUETOOTH_SCAN 替代位置权限,专门用于搜索 Android 12+ 新增
BLUETOOTH_CONNECT 替代 BLUETOOTH,专门用于连接 Android 12+ 新增

简单来说:如果你适配 Android 12+,请使用 BLUETOOTH_SCANBLUETOOTH_CONNECT,别再依赖位置权限了。我最近重构一个老项目时,发现很多开发者还在用旧权限,结果在 Android 13 上直接崩溃。

总结一下本章重点:
  • BluetoothAdapter 是蓝牙操作的入口,先判空再使用
  • 开启蓝牙推荐用 Intent 方式,别偷偷摸摸
  • 搜索设备用 startDiscovery(),记得停止搜索再连接
  • 配对用 createBond(),复杂场景用广播手动处理
  • Android 12+ 权限变了,赶紧更新你的代码

下一章,咱们开始真正建立蓝牙连接,传输数据。准备好了吗?