Android ble藍芽開發
BLE介紹
安卓4.3(API 18)為BLE的核心功能提供平臺支援和API,App可以利用它來發現裝置、查詢服務和讀寫特性。相比傳統的藍芽,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.3(API 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。你必須實現它的回撥函式,那就是返回的掃描結果。因為掃描非常消耗電量,你應當遵守以下準則:
1·只要找到所需的裝置,停止掃描。
2·不要在迴圈裡掃描,並且對掃描設定時間限制。以前可用的裝置可能已經移出範圍,繼續掃描消耗電池電量。
以下程式碼顯示如何掃描裝置和停止掃描裝置
// 10秒後停止尋找.private static final long SCAN_PERIOD = 10000;private void scanLeDevice(final boolean enable) {if (enable) {// 經過預定掃描期後停止掃描mHandler.postDelayed(new Runnable() {@Overridepublic 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 services的UUID物件陣列。
掃描的資訊在LeScallCallback裡面返回
private BluetoothAdapter.LeScanCallback mLeScanCallback =new BluetoothAdapter.LeScanCallback() {//device 裡面包含裝置的mac地址和裝置的名稱//scanRecord裡面就是ble裝置發出的廣播包資料//rssi表示ble裝置的訊號值,該值為負數,值越大表示訊號值越好@Overridepublic void onLeScan(final BluetoothDevice device, int rssi,byte[] scanRecord) {runOnUiThread(new Runnable() {@Overridepublic 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() {@Overridepublic 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分為三部分Service、Characteristic、Descriptor,這三部分都由UUID作為唯一標示符。一個藍芽4.0的終端可以包含多個Service,一個Service可以包含多個Characteristic,一個Characteristic包含一個Value和多個Descriptor,一個Descriptor包含一個Value。service是characteristic的集合.一個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);設定完成後回撥
@Overridepublic 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) {
//傳送資料成功啦啦啦
}
如何裝置回覆資料則會回撥
@Overridepublic 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類似
最後附上一個Nordic 公司開源的Android藍芽開發封裝好的庫地址
https://github.com/NordicSemiconductor/Android-BLE-Library
參考:http://www.cnblogs.com/cxk1995/p/5693979.html