Android ble藍牙開發介紹以及遇到的坑

Android ble藍牙開發

BLE介紹

安卓4.3(API 18)BLE的核心功能提供平臺支持和APIApp可以利用它來發現設備、查詢服務和讀寫特性。相比傳統的藍牙,BLE更顯著的特點是低功耗。這一優點使AndroidApp可以與具有低功耗要求的BLE設備通信,如近距離傳感器、心臟速率監視器、健身設備等。

BLE開發

BLE權限添加

爲了在app中使用藍牙功能,必須聲明藍牙權限BLUETOOTH。利用這個權限去執行藍牙通信,例如請求連接、接受連接、和傳輸數據。如果想讓你的app啓動設備發現或操縱藍牙設置,必須聲明BLUETOOTH_ADMIN權限。注意:如果你使用BLUETOOTH_ADMIN權限,你也必須聲明BLUETOOTH權限。在你的app manifest文件中聲明藍牙權限。

設置BLE

你的app能與BLE通信之前,你需要確認設備是否支持BLE,如果支持,確認已經啓用。雖然現在的手機基本都支持BLE,但是考慮到程序的健碩性,如果設置爲false,這個檢查是必需的。

BluetoothAdapter類介紹

獲取:所有的藍牙活動都需要藍牙適配器。BluetoothAdapter代表設備本身的藍牙適配器(藍牙無線)。整個系統只有一個藍牙適配器,而且你的app使用它與系統交互。下面的代碼片段顯示瞭如何得到適配器。注意該方法使用getSystemService()]返回BluetoothManager,然後將其用於獲取適配器的一個實例。Android 4.3API 18)引入BluetoothManager

// 初始化藍牙適配器

final BluetoothManager bluetoothManager =

(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);

mBluetoothAdapter = bluetoothManager.getAdapter();

有了mBluetoothAdapter之後就可以判斷當前藍牙開關狀態、藍牙未開啓情況下代碼裏面自動開啓藍牙、以及掃描周邊的ble設備

開啓藍牙

接下來,你需要確認藍牙是否開啓。調用isEnabled())去檢測藍牙當前是否開啓。如果該方法返回false,藍牙被禁用。下面的代碼檢查藍牙是否開啓,如果沒有開啓,可以提示用戶去設置開啓藍牙。

// 確保藍牙在設備上可以開啓

if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {

//藍牙未開啓

}

發現BLE設備

爲了發現BLE設備,使用startLeScan())方法。這個方法需要一個參數BluetoothAdapter.LeScanCallback。你必須實現它的回調函數,那就是返回的掃描結果。因爲掃描非常消耗電量,你應當遵守以下準則:

只要找到所需的設備,停止掃描。

不要在循環裏掃描,並且對掃描設置時間限制。以前可用的設備可能已經移出範圍,繼續掃描消耗電池電量。

以下代碼顯示如何掃描設備和停止掃描設備

// 10秒後停止尋找.

private static final long SCAN_PERIOD = 10000;

private void scanLeDevice(final boolean enable) {

if (enable) {

// 經過預定掃描期後停止掃描

mHandler.postDelayed(new Runnable() {

@Override

public void run() {

mScanning = false;

mBluetoothAdapter.stopLeScan(mLeScanCallback);

}

}, SCAN_PERIOD);

mScanning = true;

mBluetoothAdapter.startLeScan(mLeScanCallback);

} else {

mScanning = false;

mBluetoothAdapter.stopLeScan(mLeScanCallback);

}

...

}

...

}

如果你只想掃描指定類型的外圍設備,可以改爲調用startLeScan(UUID[], BluetoothAdapter.LeScanCallback)),需要提供你的app支持的GATT servicesUUID對象數組。

掃描的信息在LeScallCallback裏面返回

private BluetoothAdapter.LeScanCallback mLeScanCallback =

new BluetoothAdapter.LeScanCallback() {

//device 裏面包含設備的mac地址和設備的名稱

//scanRecord裏面就是ble設備發出的廣播包數據

//rssi表示ble設備的信號值,該值爲負數,值越大表示信號值越好

@Override

public void onLeScan(final BluetoothDevice device, int rssi,

byte[] scanRecord) {

runOnUiThread(new Runnable() {

@Override

public void run() {

mLeDeviceListAdapter.addDevice(device);

mLeDeviceListAdapter.notifyDataSetChanged();

}

});

}

};

連接到GATT服務端

與一個BLE設備交互的第一步就是連接它——更具體的,連接到BLE設備上的GATT服務端。爲了連接到BLE設備上的GATT服務端,需要使用connectGatt( )方法。這個方法需要三個參數:一個Context對象,自動連接(boolean,表示只要BLE設備可用是否自動連接到它),和BluetoothGattCallback調用。

mBluetoothGatt = device.connectGatt(this, false, mGattCallback);

連接到GATT服務端時,由BLE設備做主機,並返回一個BluetoothGatt實例,然後你可以使用這個實例來進行GATT客戶端操作。請求方(Android app)GATT客戶端。BluetoothGattCallback用於傳遞結果給用戶,例如連接狀態,以及任何進一步GATT客戶端操作。

private final BluetoothGattCallback mGattCallback =

new BluetoothGattCallback() {

@Override

public void onConnectionStateChange(BluetoothGatt gatt, int status,

int newState) {//當連接狀態發生改變

String intentAction;

if (newState == BluetoothProfile.STATE_CONNECTED) {//當藍牙設備已經連接

//獲取ble設備上面的服務

Log.i(TAG, "Attempting to start service discovery:" +

mBluetoothGatt.discoverServices());

} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {//當設備無法連接

}

}

@Override

//調用discoverServices後的回調

public void onServicesDiscovered(BluetoothGatt gatt, int status) {

if (status == BluetoothGatt.GATT_SUCCESS) {

//獲取服務成功

} else {

Log.w(TAG, "onServicesDiscovered received: " + status);

}

}

@Override

// 讀寫特性

public void onCharacteristicRead(BluetoothGatt gatt,

BluetoothGattCharacteristic characteristic,

int status) {

if (status == BluetoothGatt.GATT_SUCCESS) {

}

}

...

};

...

}

發送數據

首先通過UUID拿到對應的服務,再通過UUID拿到服務的特徵,設置特徵的屬性是BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE。設置成功後可以在該特徵值上發送數據到ble設備和接收ble設備的數據。看到這裏也許各位不熟ble開發和剛ble開發的看官也許就一臉懵逼,我只是想發送數據到ble設備,怎麼一下子搞出個UUID 服務和特徵值了,難道就不能和B/S開發一樣,連接之後我把數據發送到一個接口,服務器端就返回我需要的數據那麼簡單。這還得從ble藍牙的架構說起。

BLE分爲三部分ServiceCharacteristicDescriptor,這三部分都由UUID作爲唯一標示符。一個藍牙4.0的終端可以包含多個Service,一個Service可以包含多個Characteristic,一個Characteristic包含一個Value和多個Descriptor,一個Descriptor包含一個Valueservicecharacteristic的集合.一個characteristic包括一個單一變量和0-n個用來描述characteristic變量的descriptor.Descriptor用來描述characteristic變量的屬性。例如,一個descriptor可以規定一個可讀的描述,或者一個characteristic變量可接受的範圍,或者一個characteristic變量特定的測量單位。一般來說,Characteristic是手機與BLE終端交換數據的關鍵.

舉個栗子:當我們想要用手機與BLE設備進行通信時,實際上也就相當於我們要去找一個學生交流,首先我們需要搭建一個管道,也就是我們需要先獲取得到一個BluetoothGatt,其次我們需要知道這個學生在哪一個班級,學號是什麼,這也就是我們所說的serviceUUID,和charUUID。這裏我們還需要注意一下,找到這個學生後並不是直接和他交流,他就好像一箇中介一樣,在手機和BLE終端設備之間幫助這兩者傳遞着信息,我們手機所發數據要先經過他,在由他傳遞到BLE設備上,而BLE設備上的返回信息,也是先傳遞到他那邊,然後手機再從他那邊進行讀取。

在發送數據之前需先設置特徵的具有notificaion功能

private BluetoothGatt mBluetoothGatt;

BluetoothGattCharacteristic characteristic;

boolean enabled;

mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);

BluetoothGattDescriptor descriptor = characteristic.getDescriptor(

UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));

descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)

mBluetoothGatt.writeDescriptor(descriptor);

設置完成後回調

@Override

public final void onDescriptorWrite(final BluetoothGatt gatt, final BluetoothGattDescriptor descriptor, final int status) {

//設置成功

if (status == BluetoothGatt.GATT_SUCCESS) {

}

}

設置成功後就開始發送數據了。

//將指令放置進特徵中

characteristic.setValue(data);

//設置回覆形式characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);

//開始寫數據

mBluetoothGatt.writeCharacteristic(chharacteristic);

寫入數據成功後回調

protected void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {

//發送數據成功啦啦啦

}

如何設備回覆數據則會回調

@Override

public final void onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {

}

關閉客戶端App

當你的app完成BLE設備的使用後,應該調用close( )),系統可以合理釋放佔用資源。

public void close() {

if (mBluetoothGatt == null) {

return;

}

mBluetoothGatt.close();

mBluetoothGatt = null;

}

最後分享我在BLE 開發中遇到的坑和一些經驗

1 在所有藍牙的回調中不要操作UI。我是不會告訴你我是怎麼發現這個坑的。

2 在所有的藍牙回調中不要執行耗時操作。

3 發送數據要等到上一條數據發送成功後再發下一條數據,畢竟BLE設備運算沒有手機快,這裏可以推薦一個開源藍牙連接工具https://github.com/NordicSemiconductor/Android-nRF-Toolbox,裏面非常好的對發送的數據做了一個數據隊列。

4 合理的控制掃描過程,一般出現133錯誤的時候重連就可以先去掃描再去連接。若掃描不到時不要馬上又去掃描,不然你把手機放那一夜,把設備遠離它,第二天回來看手機時會驚喜的發現手機沒電自動關機了

遇到的坑

1 斷線重連的時候總是報133錯誤,

斷線後不要馬上去連接.先掃描設備,掃描到設備後再去連接。

2 掃描不到設備

手動關閉藍牙再打開藍牙開關。這個可能是重連裏面的掃描引起的,如果設備未在周邊,一直去掃描的話,後來設備在身邊也可能掃描不到設備。如果未能連接設備,也不能一直去掃描。掃描不到設備時說明設備並不到周邊,可以延遲多少時間後再去掃描

3 連接設備後發送數據,發送數據的回調函數也已經走了。沒有接收到數據

查看設置特徵值的描述值

descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)

mBluetoothGatt.writeDescriptor(descriptor)的回調裏面是不是回調成功了

4反覆斷開藍牙後再重連導致連接失敗

斷開藍牙後應該調用close()方法釋放資源.連接時應該設置超時,在超時時間內繼續去連接,基本低、中、高端機都能重新連接上。

5 連接上之後自動斷開連接,重連上之後又自動斷開連接,如此反覆。

我們的BLE設備在某些低端機會遇到這種問題。聽固件工程師說是BLE設備藍牙芯片頻率和手機藍牙頻率問題,需調BLE設備頻率。遇到這種問題APP就束手無策了。

6 反覆操作斷開和連接導致系統藍牙掛掉(無響應)

基本也是沒有合理釋放資源導致

7 調用掃描操作導致APP無響應

查看系統藍牙是否掛掉了。基本和問題6類似

參考文章:

http://www.cnblogs.com/cxk1995/p/5693979.html

https://developer.android.com/guide/topics/connectivity/bluetooth-le.html#roles

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章