【Android】Bluetooth(蓝牙)连接与数据传输(一)

2年前 (2022) 程序员胖胖胖虎阿
354 0 0

目录

  • 简介
  • 权限声明
  • 蓝牙扫描
      • 开始扫描
      • 取消扫描
  • 获取蓝牙信息
  • 蓝牙配对
      • 配对
      • 取消配对
  • 获取已配对蓝牙
  • 最终效果

简介

蓝牙技术是一种无线数据和语音通信开放的全球规范,它是基于低成本的近距离无线连接,为固定和移动设备建立通信环境的一种特殊的近距离无线技术(使用2.4~2.485GHz的ISM波段的UHF无线电波)连接。

蓝牙类型 描述
经典蓝牙(Classic Bluetooth) 功耗高,传输数据量大,传输距离短(10米)
低功耗蓝牙(Bluetooth Low Energy) 功耗低,传输数据量小,传输距离较经典蓝牙远
蓝牙模块 描述
单模蓝牙 有一种蓝牙版本,运行一种蓝牙协议栈的模块,常用在低功耗蓝牙(Bluetooth Low Energy),如手环。
双模蓝牙 内置两个蓝牙版本,运行两套协议栈的蓝牙模块,支持经典蓝牙与低功耗蓝牙,如手机。

权限声明

Android 11(API 30)及以下的Android版本,只需声明android.permission.BLUETOOTHandroid.permission.ACCESS_FINE_LOCATION,如果是Android 9(API 28)及以下的Android版本,则需要将android.permission.ACCESS_FINE_LOCATION更换为android.permission.ACCESS_COARSE_LOCATION。以下权限则是Android 12(API 31)及以上你可能会使用到的权限。

<!--  允许应用程序连接到配对的蓝牙设备  -->
<uses-permission android:name="android.permission.BLUETOOTH"/>
<!--  需要能够发现和配对附近的蓝牙设备  -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
<!--  允许应用程序发现和配对蓝牙设备  -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<!--  需要能够连接到配对的蓝牙设备  -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
<!--  允许应用程序在没有用户交互的情况下配对蓝牙设备,并允许或禁止电话簿访问或消息访问  -->
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED"
    tools:ignore="ProtectedPermissions" />
<!--  需要能够向附近的蓝牙设备做广告 Android 12 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE"/>

<uses-permission android:name="com.google.android.things.permission.MANAGE_BLUETOOTH" />
<!--  获得定位  -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

这里展示了七个与蓝牙相关的权限,其中的android.permission.BLUETOOTH_SCANandroid.permission.BLUETOOTH_CONNECTandroid.permission.BLUETOOTH_ADVERTISEandroid.permission.ACCESS_FINE_LOCATION权限属于危险权限,需要另外写代码去动态申请。

蓝牙扫描

开始扫描

蓝牙扫描接收扫描结果使用BroadcastReceiver进行接收,而BroadcastReceiver需要写代码去触发,执行startDiscovery()扫描蓝牙前应加上registerReceiver(BroadcastReceiver receiver,IntentFilter filter)注册一个广播接收者,同时加上IntentFilter过滤掉那些用不到的intent,只取我们用得到的Intent

val filter = IntentFilter()
// 找到设备
filter.addAction(BluetoothDevice.ACTION_FOUND)
// 远程设备的绑定状态发生变化
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED)
// 第一次检索远程设备的友好名称,或自上次检索后更改
filter.addAction(BluetoothDevice.ACTION_NAME_CHANGED)
// 远程设备的蓝牙类已更改
filter.addAction(BluetoothDevice.ACTION_CLASS_CHANGED)
// 用于在获取远程设备后将其UUID 作为ParcelUuid远程设备的包装进行广播
filter.addAction(BluetoothDevice.ACTION_UUID)
// 操作配对请求
filter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST)
// 蓝牙状态改变
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED)
// 开始搜索蓝牙
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED)
// 蓝牙搜索完成
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
// 本地Adapter的蓝牙扫描模式发生了变化
filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED)
// 本地蓝牙适配器更改蓝牙名称
filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED)
// 请求本地蓝牙可被其它蓝牙扫描到
filter.addAction(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE)
// 显示请求可发现模式的系统活动
filter.addAction(BluetoothAdapter.ACTION_REQUEST_ENABLE)
// 连接状态改变
filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)

继承了BroadcastReceiver的类,实现其onReceive(Context context, Intent intent)方法,就可以在onReceive(Context context, Intent intent)里加上if语句去判断当前BroadcastReceiver是否接收到了上述的Action,当接收到IntentAction与上述IntentFilter.addAction(String action)相符,意味着是时候该执行与对应Action相关的操作了。

当识别到设备需要将其返回到Activity时,则可使用EventBus来进行传输。

执行上述操作后,当前手机开启了蓝牙,其它的蓝牙设备却扫描不到当前手机蓝牙时,也许是因为没有启用允许其它蓝牙发现当前手机蓝牙的功能(Android系统默认情况下无法扫描到当前手机蓝牙),关于这一点,应在扫描蓝牙前就加上允许其它蓝牙发现当前手机蓝牙的代码,如下:

val enabler = Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE)
startActivity(enabler)

执行代码,弹出申请是否允许其它设备发现本设备功能弹框,点击允许后,在接下来的120秒内(上限为300秒),其它的蓝牙可在规定时间内扫描到当前手机蓝牙。
【Android】Bluetooth(蓝牙)连接与数据传输(一)

取消扫描

取消蓝牙扫描时应先判断是否为null、是否正在扫描蓝牙后再取消扫描,同时注销掉广播。

if (bluetoothAdapter != null && bluetoothAdapter!!.isDiscovering) {
    bluetoothAdapter!!.cancelDiscovery()
    context.unregisterReceiver(bluetoothReceiver)
}

获取蓝牙信息

找到蓝牙设备后可得到一个任意类型T,将其转换为BluetoothDevice,就可以通过这一个类来获取蓝牙的相关信息,下图中使用到的方法如下:

Methods Describe
getName() 获取蓝牙名称
getType() 获取蓝牙类型
getBondState() 获取蓝牙的绑定状态
getAddress() 获取蓝牙设备的硬件地址
getUuids() 返回远程设备支持的功能 (UUID)

其效果如下:
【Android】Bluetooth(蓝牙)连接与数据传输(一)

其它获取蓝牙相关信息方法见:BluetoothDevice

蓝牙配对

配对

蓝牙的配对使用BluetoothDevice.createBond()进行配对,createBond()有一个返回值,但千万不要以为他返回true意味着蓝牙配对成功,返回true只是意味着它开始申请配对,届时双方的蓝牙都会弹出如下对话框提示是否配对,此刻点击取消才是真正的取消配对,即配对失败。

【Android】Bluetooth(蓝牙)连接与数据传输(一)
部分蓝牙,如蓝牙耳机、部分手机蓝牙,配对成功后就直接连接上了,不需要再执行连接的操作。关于连接、数据传输的操作会在下一章进行讲解,博主暂时还缺乏能够进行数据传输的蓝牙设备,还请见谅。

取消配对

蓝牙配对与取消配对的方法createBond()removeBond()都在BluetoothDevice里面,但调用方式却有着天壤之别。

createBond()可通过BluetoothDevice类的实例直接进行调用,removeBond()通过BluetoothDevice实例来进行调用是行不通的,这一切的始作俑者,都是因为removeBond()@SystemApi注解导致的。

【Android】Bluetooth(蓝牙)连接与数据传输(一)
但这并不意味无法调用removeBond()方法,针对这种情况,可以使用反射来强制调用removeBond()方法,如下所示:

try {
     val method = BluetoothDevice::class.java.getMethod("removeBond")
     val b = method.invoke(device) as Boolean
 } catch (e: Exception) {
     e.printStackTrace()
 }

获取已配对蓝牙

获取已经配对的蓝牙设备,得拿到BluetoothAdapter类的实例,调用getBondedDevices()即可获得一个Set<BluetoothDevice>类型的已配对蓝牙数据。

val bondedDevices: Set<BluetoothDevice> get() = getBluetoothAdapter()!!.bondedDevices

【Android】Bluetooth(蓝牙)连接与数据传输(一)

最终效果

由于


【Android】Bluetooth(蓝牙)连接与数据传输(一)
【Android】Bluetooth(蓝牙)连接与数据传输(一)


注意事项:
1、低功耗蓝牙不能兼容(连接)经典蓝牙,只能兼容双模蓝牙、低功耗蓝牙。
2、BluetoothAdapter.getDefaultAdapter()方法在API 31 中已废弃,可使用BluetoothManager.getAdapter()代替。
3、本项目目前只做到蓝牙扫描与配对,数据的传输因缺少对应的蓝牙设备而暂时停滞,后期有相关设备后将会补上数据传输相关的文章。

点击前往下载源码

参考文档:
1、Android Bluetooth 连接
2、Android Developers —— Bluetooth overview(蓝牙概述)
3、Android Developers —— Bluetooth permissions(蓝牙权限)
4、Android Developers —— Manifest.permission(蓝牙清单权限)
5、Android Developers —— Find Bluetooth devices(查找蓝牙设备)
6、Android Developers —— Connect Bluetooth devices(连接蓝牙设备)

相关文章

暂无评论

暂无评论...