Android藍芽開發之低功耗藍芽(藍芽4.0)開發全記錄
主要內容概況
前面我記錄了開發藍芽2.0的過程,今天準備介紹現在的主流藍芽技術,藍芽4.0的開發,該藍芽技術是在Android4.3(API級別18)版本引入的API。
具體的區別主要以下幾點:
1.新的藍芽技術提供了連線服務的方法,以前是沒有提供連線藍芽的方法的。
2.掃描和連線裝置,通過回掉的方式來進行通知,不再是以前通過系統廣播的方式。
3.低功耗說明使用4.0的藍芽能降低電量消耗。
4.資料互動的方式也不再是通過stock的方式,而是通過GATT協議。
開始介紹主要的開發內容
開發過程主要還是參考谷歌的官方案例,samples很簡單主要就四個類。
下面開始正式上程式碼:
-
常規的,開啟藍芽,許可權新增,設定裝置可見等都不介紹了,可以看一下我以前那一篇文章。
我們從裝置掃描開始介紹,程式碼如下:
//獲取一下藍芽介面卡
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
//開始進行掃描裝置了,傳遞進去一個監聽
mBluetoothAdapter.startLeScan(mLeScanCallback);
//再回掉裡去接收掃描到的裝置
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
runOnUiThread(new Runnable() {
@Override
public void run() {
//這裡要注意,會掃描到相同的裝置,需要對列表進行去重複
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
};
//在add的方法中需要進行判斷
public void addDevice(BluetoothDevice device) {
if (!mLeDevices.contains(device)) {
mLeDevices.add(device);
}
}
以上基本上就可以完成掃描裝置,並且顯示刀列表上了。是不是很簡單,連線裝置部分會比較麻煩一些。
- 連線裝置部分主要可以分為兩個部分,一部分先是進行裝置匹配;另一部分則是進行連線服務。
官方的demo上繫結一個服務,然後一些連線操作都放到服務中進行,實際我們可以直接呼叫連線裝置的方法。
//連線設定,並傳入一個連線的回掉。
mBluetoothGatt = device.connectGatt(this, false, mBluetoothGattCallback);
//6.0的裝置中添加了新的引數,註解意思是首選連線的遠端裝置的型別,這裡我們選擇的是低功率藍芽裝置
mBluetoothGatt = device.connectGatt(this, false, mBluetoothGattCallback,BluetoothDevice.TRANSPORT_LE);
然後主要的程式碼邏輯都是在我們的監聽事件上,收到連線成功的監聽,要去解析服務,這裡要做的事情還有要檢視連結的裝置是否支援有對應的服務在BluetoothGatt裡面。
下面這裡上具體的程式碼,還是參照官方例子,稍微改進一下。
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
LogUtil.i("連結上");
//連線上裝置更新一下UI相關
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
LogUtil.i("斷開連結");
//斷開裝置也更行一下,如果需要重連線,可以在這裡進行。
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
LogUtil.i("解析服務");
//獲取到支援的服務列表,下面程式碼塊進行解析
displayGattServices(mBluetoothLeService.getSupportedGattServices());
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(characteristic);
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
mConnectionState = STATE_CONNECTED;
broadcastUpdate(characteristic);
}
};
- 具體解析服務的地方主要是對裝置的一個匹配,中心裝置和外圍裝置有相同的UUID然後就可以識別上了。有一些常見的UUID可以瞭解一下。
//這裡解析服務相關。主要是兩個迴圈,一個是匹配服務的UUID,另外一個則是匹配特徵的UUID,如果我們只匹配特定的裝置,這裡的UUID可以是一個固定的String(UUID)字串。
for (BluetoothGattService gattService : gattServices) {
HashMap<String, String> currentServiceData = new HashMap<String, String>();
//獲取每個服務的uuid
uuid = gattService.getUuid().toString();
//匹配我們的uuid,只要不匹配的姐跳過繼續匹配
if (!"某一個uuid".equals(s.getUuid().toString())) {
continue;
}
// 上面服務匹配成功後,在匹配特徵,其實和上面一樣,拿到特定的uuid,匹配上後就可以傳送資料了
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
uuid = gattCharacteristic.getUuid().toString();
//匹配我們的uuid,只要不匹配的姐跳過繼續匹配
if (!"某一個uuid".equals(s.getUuid().toString())) {
continue;
}
//傳送特徵通知,是否啟用還特徵裝置通知。
setCharacteristicNotification(bc, true);
}
}
這裡連線成功後,再把我們的具有我們指定特徵的設備註冊到遠端的裝置上去,這裡我理解是給遠端裝置發放了一個通行證,這樣你的資料才能傳送到我(手機或中心裝置)的上面。
/**
* Enables or disables notification on a give characteristic.
*
* @param characteristic Characteristic to act on.
* @param enabled If true, enable notification. False otherwise.
*/
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
// This is specific to Heart Rate Measurement.
if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
//構造出一個gatt的描述符號,註冊到
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
//Write the value of a given descriptor to the associated remote device.
//這裡就是類似的把描述符寫到遠端的裝置上了。
mBluetoothGatt.writeDescriptor(descriptor);
}
}
-
完成上面所有的步驟,基本上中心裝置和遠端裝置已經可以進行資料的傳遞了。
當發生資料變化的時候回去回掉我們最開始註冊的mGattCallback方法中的實現。然後收到通知或者是read的訊息的時候,獲取到收到的資料。
//mGattCallback中的以下兩個方法在發生資料變化的時候會被呼叫。然後我們在這裡就可一拿到傳送過來的資料了。 @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(characteristic); } } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { mConnectionState = STATE_CONNECTED; broadcastUpdate(characteristic); }
-
呼叫broadcastUpdate方法統一處理收到的通知或者資料,下面我們其實只關注自己指定的UUID裝置就可以了,因為我們自己的應用也只是收集自己的遠端裝置的訊息資料。
private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) { final Intent intent = new Intent(action); // This is special handling for the Heart Rate Measurement profile. Data parsing is // carried out as per profile specifications: // http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) { int flag = characteristic.getProperties(); int format = -1; if ((flag & 0x01) != 0) { format = BluetoothGattCharacteristic.FORMAT_UINT16; Log.d(TAG, "Heart rate format UINT16."); } else { format = BluetoothGattCharacteristic.FORMAT_UINT8; Log.d(TAG, "Heart rate format UINT8."); } final int heartRate = characteristic.getIntValue(format, 1); Log.d(TAG, String.format("Received heart rate: %d", heartRate)); intent.putExtra(EXTRA_DATA, String.valueOf(heartRate)); } else { // For all other profiles, writes the data formatted in HEX. //對於其他的遠端裝置,我們不關心,這裡可以不用實現 } } //這裡我們直接拿到上面heartRate資料就可了。拿到資料後就可以回到我們正常的業務邏輯。 sendBroadcast(intent); }
完成上述的幾個大的步驟基本上你的遠端裝置和手機已經連線上了,總體來說整個過程還是比較容易。
藍芽的內容也就到此結束了,如果有問題歡迎反饋給我,其實這個過程就是參考谷歌的官方demo來機率。當我們接觸一個新的開發領域的時候,首先想到的應該就是跟著官方文件和demo走,防止自己誤入歧途