蓝牙,想必大家都不陌生,它是一种无线通讯技术标准,用于短距离间交换数据。
蓝牙主要分为经典蓝牙和低功耗蓝牙,蓝牙 3.0 以下版本称为经典蓝牙,功耗高,传输数据量大,有效距离 10 米左右,蓝牙 4.0 及以上为低功耗蓝牙,简称 BLE 蓝牙,功耗低,传输数据量相对较小,有效距离 50 米左右。因为现在的蓝牙基本上都是 5.0+ 了,所以这里主要讲解 BLE 蓝牙的开发。Android 4.3 为蓝牙低功耗引入内置平台支持,并提供了相应的 API 。
蓝牙权限
xml
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
其中 ACCESS_COARSE_LOCATION 和 ACCESS_FINE_LOCATION 属于危险权限,需要动态申请,这里不再赘述。位置是必须的权限,否则是扫描不出结果的。
启用蓝牙
首先需要获取 BluetoothAdapter,它是设备的蓝牙适配器,系统只有一个蓝牙适配器,我们需要使用此对象进行交互。
kotlin
private val bluetoothAdapter: BluetoothAdapter? by lazy(LazyThreadSafetyMode.NONE) {
val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
bluetoothManager.adapter
}
启用蓝牙时,需先判断一下蓝牙是否已经启用,如果没有的话则启用它。需要注意的是,这里使用的 registerForActivityResult 要写在 onCreate 方法里,不然会报错。
kotlin
val bluetoothLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == Activity.RESULT_OK) {
// TODO: Bluetooth enabled successfully
}
}
bluetoothAdapter?.takeUnless { it.isEnabled }?.let {
val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
bluetoothLauncher.launch(intent)
}
查找设备
因为蓝牙扫描比较耗电,所以应遵循以下准则:
- 找到所需设备后,立即停止扫描。
- 绝对不进行循环扫描,并设置扫描时间限制。之前可用的设备可能已超出范围,继续扫描会耗尽电池电量。
kotlin
lifecycleScope.launch(Dispatchers.IO) {
// 开启扫描
bluetoothAdapter?.bluetoothLeScanner?.startScan(scanCallback)
// 扫描时间限制
delay(10000)
// 停止扫描
bluetoothAdapter?.bluetoothLeScanner?.stopScan(scanCallback)
}
这里会接收一个 ScanCallback 回调
kotlin
private val scanCallback = object : ScanCallback() {
// 当扫描到蓝牙设备时,这个方法会被调用。其中 callbackType 参数表示扫描类型,result 参数表示扫描到的设备信息。
override fun onScanResult(callbackType: Int, result: ScanResult?) {
if (result != null) {
val device = result.device
Log.i(TAG, "device.name:${device?.name}, address:${device.address}")
}
}
// 当批量扫描到多个蓝牙设备时,这个方法会被调用。其中 results 参数表示扫描到的设备列表。
override fun onBatchScanResults(results: MutableList<ScanResult>?) {
Log.i(TAG, "results size: ${results?.size}")
}
// 当蓝牙扫描失败时,这个方法会被调用。其中 errorCode 参数表示扫描失败类型。
override fun onScanFailed(errorCode: Int) {
Log.i(TAG, "onScanFailed: $errorCode")
}
}
在一般情况下,BLE 蓝牙只能扫描到支持 BLE 协议的设备,而不能直接扫描到传统蓝牙设备。某些设备具有双模功能,既支持传统蓝牙也支持 BLE,这样的设备可以通过 BLE 来进行扫描,并且可以同时扫描到支持 BLE 和传统蓝牙的设备。