这是我参与8月更文挑战的第31天,活动详情查看:8月更文挑战
Android BLE蓝牙——主机开发
Android 一般都是作为主机去连接从机,比如蓝牙耳机、物联网设备这些都是从机,从机可以设置一些参数比如广播内容和频率等,主机也可以设置一些配置,但在Android中有些坑需要记录一下。
一、什么是主机
在蓝牙中,主动连接其它设备的是主机
主设备模式:工作在主设备模式,可以与一个从设备进行连接。在此模式下可以对周围设备进行搜索并选择需要连接的从设备进行连接。理论上,一个蓝牙主端设备,可同时与多个蓝牙从端设备进行通讯。
二、在Android中开发主机
2.1 流程图
正常情况下,我们与从机的通讯流程是这样。 正因为有这些流程,在Android中复杂的地方就在于所有的操作流程几乎都要等回调。 如果有一对多的情况,所有的回调接口还需要装在集合中。
graph LR
A[扫描] -->B(显示设备列表)
B --> C(选择单个设备)
C --> D(连接)
D --> E(发送数据)
2.2 权限
权限依旧少不了,在有些手机上,不打开位置权限,会扫描不到从机的设备。
<!-- 蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- LE Beacons位置相关权限 -->
<uses-permission android:name="android.perm ission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
2.3 扫描
在Android 5.0 以下的扫描方式
//开始扫描
mBluetoothAdapter.startLeScan(mLeScanCallback);
//停止扫描
mBluetoothAdapter.stopLeScan(mLeScanCallback);
在Android 5.0及以上的扫描方式
ScanSettings.Builder settingBuilder = new ScanSettings.Builder();
settingBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY); // 低延迟。
ScanSettings settings = settingBuilder.build();
BluetoothLeScanner scanner = bluetoothAdapter.getBluetoothLeScanner();
scanner.startScan(scanFilterList, settings, scanCallback);
其中 setScanMode
可以设置功率,设置为低延迟可以很快的扫描到从机,而改为省点模式,扫描到从机的速度大大降低。
scanFilterList
中保存着从机的过滤条件,比如指定UUID,指定蓝牙名称等
List<ScanFilter> scanFilterList = new ArrayList<>();
ScanFilter.Builder builder = new ScanFilter.Builder();
builder.setDeviceName("XXX");
builder.setServiceUuid(new ParcelUuid(AppConfig.UUID_SERVICE));
ScanFilter scanFilter = builder.build();
scanFilterList.add(scanFilter);
另外还有两点需要注意的地方。
- 蓝牙频繁扫描多次,会出现扫描不到设备的情况,过一段时间会恢复。
- 蓝牙扫描时长默认的时间是30秒,并且没有地方设置。想要更改扫描时间,就要维护一个定时器,达到某个时间之后,手动关闭扫描。
2.4 连接
连接从机Android提供了通过mac地址去连接从机的方法,mac地址在哪里能获取到呢,其实在扫描到设备后,我们就能拿到mac地址。
并且,连接方法也需要进行版本判断,否则很容易出现 链接出错,错误码 133的错误
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
gatt = bluetoothDevice.connectGatt(mContext, false, mBleGattCallBack, TRANSPORT_LE);
} else {
gatt = bluetoothDevice.connectGatt(mContext, false, mBleGattCallBack);
}
此外,通过系统的连接状态回调,我们可以在此基础上增加连接失败后重试的机制。
2.5 发送数据
我看见大部分的文章都是说BLE不适合传递大量数据,默认一包只能传20个字节,我不这么认为。
实际上是默认传20个字节,但是最终确定一包可以传多少,申请MTU得到的数据才是最终一包可传递的数量。
MTU 怎么得到的
gatt.requestMtu(XX)
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
//TODO
if (BluetoothGatt.GATT_SUCCESS == status) {
BLESdkLog.e(TAG, "设置MTU成功:" + mtu);
BLESdkConfig.getInstance().setBleMaxSplitCount(mtu - 3); // 设置MTU之后,重新分包
} else {
BLESdkLog.e(TAG, "设置MTU失败:" + mtu);
BLESdkConfig.getInstance().setBleMaxSplitCount(mtu - 3);
}
gatt.discoverServices(); //启动服务发现
}
然后如果是大量数据,就根据MTU进行分包发送。
发送时也有讲究,不要简单的循环发送,而要等到每一包成功的反馈,再发送下一包的数据。