最近接觸到了和硬件之間的藍牙交互,有一套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回調邏輯混亂。