最近接触到了和硬件之间的蓝牙交互,有一套Google给的代码,总来的说使用起来太过冗长,封装过程略微复杂。找到一个三方的框架BluetoothKit.
这个蓝牙框架使用起来较为简单。使用方法也较为简洁明了。但是BluetoothKit不能用在经典蓝牙身上,它只支持经典蓝牙的SPP搜索,并不能支持经典蓝牙的连接。而它只支持BLE设备的操作。
- GATT协议
在Android4.3(API 18)以上的设备几乎都是BLE。BLE的协议是GATT协议,关于GATT协议,我们需要了解几个关键的词汇:
①Profile:一个标准的通信协议,存在于从机中,蓝牙有一些规范的Profile,如:HID(人机交互设备),OVER(基于蓝牙的IPV6组网方式),心率计等。每个Profile中会包含多个Service,每个Service代表从机的一个能力。
②Service:在Ble从机中,有很多服务,比如:电量信息服务,系统信息服务等。一个service包含多个characteristic特征值。获取的信息值存放在从机的Profile的Characteristic。然后主机通过characteristic来获取信息数据。所以说,通信最主要的还是Characteristic。
③Characteristic:特征。读写内容都是通过Characteristic来进行的。
④UUID:统一标识码,Service和Characteristic都有自己的唯一UUID。
蓝牙协议图大致如下:
- BluetoothKit框架的一些介绍和使用方法
BluetoothKit提供:连接,读写,通知等接口。
会自动断开不活跃的蓝牙设备。
BluetoothKit的使用方法:
1.添加依赖 两种方式: ①在gradle中添加compile 'com.inuker.bluetooth:library:1.3.8' ②导入依赖Library模块
2.添加蓝牙权限
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
3.扫描蓝牙
private void searchDevice() {
SearchRequest request = new SearchRequest.Builder().searchBluetoothLeDevice(5000,2).searchBluetoothLeDevice(5000).build();
ClientManager.getClient().search(request, mSearchResponse);
}
private final SearchResponse mSearchResponse = new SearchResponse() {
@Override
public void onSearchStarted() {
//开始扫描,可以在此加入一些状态设置。以及一些数据清空处理。
mDevices.clear();
}
@Override
public void onDeviceFounded(SearchResult device) {
//监测扫描出的设备是否已经包含在设备数组里
if (!mDevices.contains(device)) {
mDevices.add(device);
//给设备Adapter设值
mAdapter.setDataList(mDevices);
}
if (mDevices.size() > 0) {
mRefreshLayout.showState(AppConstants.LIST);
}
}
@Override
public void onSearchStopped() {
BluetoothLog.w("MainActivity.onSearchStopped");
mListView.onRefreshComplete(true);
mRefreshLayout.showState(AppConstants.LIST);
}
@Override
public void onSearchCanceled() {
BluetoothLog.w("MainActivity.onSearchCanceled");
mListView.onRefreshComplete(true);
mRefreshLayout.showState(AppConstants.LIST);
}
};
4.连接蓝牙
private void connectDevice() {
BleConnectOptions options = new BleConnectOptions.Builder()
.setConnectRetry(3)//重试3次
.setConnectTimeout(5000)//5s后为连接超时
.setServiceDiscoverRetry(3)//连接Service重试3次
.setServiceDiscoverTimeout(5000)//5s后连接'服务'超时
.build();
//此处单例出BluetoothClient对象来进行连接操作
ClientManager.getClient().connect(mMac, options, new BleConnectResponse() {
@Override
public void onResponse(int code, BleGattProfile profile) {
//如果连接没有成功,则重试。
if (code == REQUEST_SUCCESS) {
setGattProfile(profile);
}else{
//检测是否需要连接设备(如果已经连接,则不需要再去连接)
connectDeviceIfNeeded();
}
}
});
}
//获取属性
public void setGattProfile(BleGattProfile profile) {
List<DetailItem> items = new ArrayList<DetailItem>();
List<BleGattService> services = profile.getServices();
for (BleGattService service : services) {
items.add(new DetailItem(DetailItem.TYPE_SERVICE, service.getUUID(), service.getUUID()));
List<BleGattCharacter> characters = service.getCharacters();
for (BleGattCharacter character : characters) {
items.add(new DetailItem(DetailItem.TYPE_CHARACTER, character.getUuid(), service.getUUID()));
}
}
//将得到的items传递到需要发送信息的位置。DetailItem是service和character的实体。
//上面我们就提到过,蓝牙间的消息传递获取是根据service的character来做到的。
}
//蓝牙的状态监听
ClientManager.getClient().registerConnectStatusListener(macAddress, mBleConnectStatusListener);
private final BleConnectStatusListener mBleConnectStatusListener = new BleConnectStatusListener() {
@Override
public void onConnectStatusChanged(String macAddress, int status) {
if (status == STATUS_CONNECTED) {
//蓝牙设备处于连接状态
} else if (status == STATUS_DISCONNECTED) {
//蓝牙设备断开
}
}
};
//注销蓝牙状态监听
ClientManager.getClient().unregisterConnectStatusListener(macAddress, mBleConnectStatusListener);
5.蓝牙硬件通信
private List<DetailItem> mDeviceDetailDataList = new ArrayList<>();
public void setDeviceDetailDataList(List<DetailItem> datas) {
mDeviceDetailDataList.clear();
//加入最新的service和character数据
mDeviceDetailDataList.addAll(datas);
BaseLoginBean baseLoginBean=loginCheck(mDeviceDetailDataList);
mService=baseLoginBean.getmService();
mCharacter=baseLoginBean.getmCharacter();
//写数据到蓝牙设备。需要得知要操作设备的mac,要操作的service和character,
//以及要传输给蓝牙设备的信息。以及传输回调。
ClientManager.getClient().write(macAddress, mService, mCharacter, baseLoginBean.getmBytes(), mWriteRsp);
//将蓝牙设备传递给我们的信息进行提示返回。需要得知要操作设备的mac,
//要操作的service和character,以及返回的信息回调。
ClientManager.getClient().notify(mMac, mService, mCharacter, mNotifyRsp);
}
在读取的时候还有一种read方法。read方法和notify有什么不一样的呢?
notify:有点类似于推送的意思,不管自己去不去读取蓝牙返回给我们的信息。始终都会得到蓝牙返回给的信息。
unnotify:不获取返回信息。
read:用户自己去获取数据,自己要获取的时候才去获取数据。
除此writeNoRsp:凡是带有WRITE_TYPE_NO_RESPONSE标识的,速率比write快2~3倍。建议用于固件升级。
writeDescriptor:对Characteristic的描述,如:范围,计量单位等。同理readDescriptor则是读取。
indicate:和notify类似。 unindicate则是不获取返回信息。
readRssi:读取蓝牙强度。
6.断开蓝牙
ClientManager.getClient().disconnect(macAddress);
Tips:
1.在某个蓝牙设备disconnect()后,再去调用该蓝牙的disconnect。依然会执行到蓝牙状态回调去。
2.使用notify的时候最好不要在一个界面去复用多次,可能会出现重复调用情况,导致notify回调逻辑混乱。