第四章 蓝牙适配器初始化:扫描本地蓝牙设备、获取适配器信息、设置设备名称与可见性
好,咱们进入实战环节的第一个硬骨头——蓝牙适配器初始化。
说实话,很多初学者一上来就急着写连接代码、搞通讯录同步,结果卡在最开始的适配器初始化上。我当年第一次做车载蓝牙项目时,就因为这个吃了不少苦头。所以这一章,咱们把基础打扎实。
4.1 蓝牙适配器是什么?
蓝牙适配器(BluetoothAdapter),说白了就是系统里那个负责管理蓝牙硬件的“管家”。
在Android系统里,所有蓝牙操作都得通过它来发起。你想扫描设备?找它。你想获取本机蓝牙地址?还是找它。你想让设备能被别人发现?也得通过它。
我习惯把蓝牙适配器理解成一个“入口”。没有这个入口,后面的一切都是空谈。
4.2 获取蓝牙适配器实例
在Android开发中,获取蓝牙适配器有两种方式。我个人推荐第二种,更稳定。
方式一:通过系统服务获取
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
这种方式最简单,一行代码搞定。但有个坑——如果设备没有蓝牙硬件,它会返回null。所以一定要判空。
方式二:通过上下文获取(我推荐)
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
为什么推荐第二种?
我在项目中遇到过一种情况:某些定制化的车载系统,getDefaultAdapter() 返回了非空对象,但实际蓝牙硬件根本没初始化完成。用 BluetoothManager 方式获取,能更准确地反映硬件状态。
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
从 Android 6.0 开始,还需要动态申请 ACCESS_FINE_LOCATION 权限。
4.3 检查并启用蓝牙
拿到适配器后,第一件事就是检查蓝牙是否开启。你想想看,如果蓝牙都没开,后面扫描、连接全是白搭。
if (bluetoothAdapter == null) {
// 设备不支持蓝牙
return;
}
if (!bluetoothAdapter.isEnabled()) {
// 蓝牙未开启,请求用户开启
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
这里有个细节:isEnabled() 返回 false 时,不要直接调用 enable() 方法强制开启。为什么?因为 enable() 是静默开启,用户完全不知情。这在车载场景下很危险——万一用户在开车,蓝牙突然开启导致音频中断怎么办?
我建议的做法是:弹出一个对话框,让用户确认。这样既合规,又安全。
4.4 扫描本地蓝牙设备
扫描设备,是蓝牙通讯的第一步。车载场景下,我们通常要扫描附近的手机,然后建立连接。
启动扫描:
bluetoothAdapter.startDiscovery();
就这么简单?嗯,表面上看是的。但实际开发中,这里藏着不少坑。
扫描结果回调:
你需要注册一个 BroadcastReceiver 来接收扫描结果:
private final BroadcastReceiver discoveryReceiver = 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 deviceName = device.getName();
String deviceAddress = device.getAddress();
// 这里可以更新UI,显示发现的设备
}
}
};
// 注册接收器
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(discoveryReceiver, filter);
停止扫描:
if (bluetoothAdapter.isDiscovering()) {
bluetoothAdapter.cancelDiscovery();
}
嗯,这里要注意:cancelDiscovery() 并不是立即停止扫描,它只是发送一个停止请求。真正的停止需要等系统处理完当前扫描周期。
4.5 获取适配器信息
获取本机蓝牙适配器的信息,在车载开发中很常用。比如你要在屏幕上显示“本机蓝牙名称:我的车机”,或者调试时需要查看蓝牙地址。
| 信息项 | 方法 | 说明 |
|---|---|---|
| 蓝牙名称 | getName() |
返回当前蓝牙名称,可修改 |
| 蓝牙地址 | getAddress() |
返回MAC地址,如 00:11:22:AA:BB:CC |
| 扫描状态 | isDiscovering() |
返回是否正在扫描 |
| 蓝牙状态 | getState() |
返回 STATE_ON、STATE_OFF 等 |
| 已绑定设备列表 | getBondedDevices() |
返回已配对的设备集合 |
举个例子,获取并显示本机信息:
String name = bluetoothAdapter.getName();
String address = bluetoothAdapter.getAddress();
int state = bluetoothAdapter.getState();
Set<BluetoothDevice> bondedDevices = bluetoothAdapter.getBondedDevices();
Log.d("BluetoothInfo", "名称: " + name);
Log.d("BluetoothInfo", "地址: " + address);
Log.d("BluetoothInfo", "状态: " + (state == BluetoothAdapter.STATE_ON ? "已开启" : "未开启"));
Log.d("BluetoothInfo", "已配对设备数: " + bondedDevices.size());
getAddress() 返回了 02:00:00:00:00:00 这样的地址。后来查资料才知道,从 Android 6.0 开始,出于隐私保护,系统会为未授权的应用返回随机地址。所以,别太依赖蓝牙地址来做设备识别。
4.6 设置设备名称与可见性
车载蓝牙有个典型场景:用户想用手机搜索车机,然后配对。这时候,车机需要有一个友好的名称,并且要处于“可被发现”状态。
设置设备名称:
bluetoothAdapter.setName("我的车载蓝牙");
这个方法会立即生效,并且重启蓝牙后名称仍然保留。不过要注意,名称长度有限制,我记得最长好像是 248 个字节(UTF-8编码)。中文名称一般没问题,但别搞太长的。
设置可见性:
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
这里 EXTRA_DISCOVERABLE_DURATION 的单位是秒。最大值是 3600 秒(1小时),设为 0 表示一直可见。但我不建议设 0,原因有两个:
- 一直可见会持续消耗电量
- 存在安全隐患,容易被恶意设备扫描到
我一般设 120 到 300 秒,够用户完成配对就行。
4.7 完整初始化流程
好了,把上面的知识点串起来,一个完整的蓝牙适配器初始化流程大概是这样的:
public void initBluetooth() {
// 1. 获取适配器
BluetoothManager manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter adapter = manager.getAdapter();
if (adapter == null) {
// 不支持蓝牙,提示用户
return;
}
// 2. 检查并启用蓝牙
if (!adapter.isEnabled()) {
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, REQUEST_ENABLE_BT);
return;
}
// 3. 设置设备名称
adapter.setName("CarBT_Device");
// 4. 设置可见性(让手机能搜到)
Intent discoverable = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverable.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 180);
startActivity(discoverable);
// 5. 开始扫描
adapter.startDiscovery();
// 6. 注册扫描结果接收器
registerReceiver(discoveryReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
}
这个流程看起来简单,但实际项目中要考虑的东西很多。比如:
- 如果用户拒绝了蓝牙开启请求怎么办?
- 扫描过程中用户退出了页面怎么办?
- 多个页面同时扫描会不会冲突?
这些都是在车载开发中会遇到的真实问题。我建议你在写代码时,把这些边界情况都考虑进去。毕竟,车载系统不像手机,用户可不想在开车时看到一堆莫名其妙的弹窗。
好,这一章就到这里。下一章咱们聊聊蓝牙配对与连接,那才是真正开始“打交道”的时候。