Android4.3 藍(lán)牙BLE初步
一、關(guān)鍵概念:
Generic Attribute Profile (GATT)
通過BLE連接,讀寫屬性類小數(shù)據(jù)的Profile通用規(guī)范。現(xiàn)在所有的BLE應(yīng)用Profile都是基于GATT的。
Attribute Protocol (ATT)
GATT是基于ATT Protocol的。ATT針對(duì)BLE設(shè)備做了專門的優(yōu)化,具體就是在傳輸過程中使用盡量少的數(shù)據(jù)。每個(gè)屬性都有一個(gè)唯一的UUID,屬性將以characteristics and services的形式傳輸。
Characteristic
Characteristic可以理解為一個(gè)數(shù)據(jù)類型,它包括一個(gè)value和0至多個(gè)對(duì)次value的描述(Descriptor)。
Descriptor
對(duì)Characteristic的描述,例如范圍、計(jì)量單位等。
Service
Characteristic的集合。例如一個(gè)service叫做“Heart Rate Monitor”,它可能包含多個(gè)Characteristics,其中可能包含一個(gè)叫做“heart rate measurement"的Characteristic。
二、角色和職責(zé):
Android設(shè)備與BLE設(shè)備交互有兩組角色:
中心設(shè)備和外圍設(shè)備(Central vs. peripheral);
GATT server vs. GATT client.
Central vs. peripheral:
中心設(shè)備和外圍設(shè)備的概念針對(duì)的是BLE連接本身。Central角色負(fù)責(zé)scan advertisement。而peripheral角色負(fù)責(zé)make advertisement。
GATT server vs. GATT client:
這兩種角色取決于BLE連接成功后,兩個(gè)設(shè)備間通信的方式。
舉例說明:
現(xiàn)有一個(gè)活動(dòng)追蹤的BLE設(shè)備和一個(gè)支持BLE的Android設(shè)備。Android設(shè)備支持Central角色,而BLE設(shè)備支持peripheral角色。創(chuàng)建一個(gè)BLE連接需要這兩個(gè)角色都存在,都僅支持Central角色或者都僅支持peripheral角色則無法建立連接。
當(dāng)連接建立后,它們之間就需要傳輸GATT數(shù)據(jù)。誰做server,誰做client,則取決于具體數(shù)據(jù)傳輸?shù)那闆r。例如,如果活動(dòng)追蹤的BLE設(shè)備需要向Android設(shè)備傳輸sensor數(shù)據(jù),則活動(dòng)追蹤器自然成為了server端;而如果活動(dòng)追蹤器需要從Android設(shè)備獲取更新信息,則Android設(shè)備作為server端可能更合適。
三、權(quán)限及feature:
和經(jīng)典藍(lán)牙一樣,應(yīng)用使用藍(lán)牙,需要聲明BLUETOOTH權(quán)限,如果需要掃描設(shè)備或者操作藍(lán)牙設(shè)置,則還需要BLUETOOTH_ADMIN權(quán)限:
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
除了藍(lán)牙權(quán)限外,如果需要BLE feature則還需要聲明uses-feature:
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
按時(shí)required為true時(shí),則應(yīng)用只能在支持BLE的Android設(shè)備上安裝運(yùn)行;required為false時(shí),Android設(shè)備均可正常安裝運(yùn)行,需要在代碼運(yùn)行時(shí)判斷設(shè)備是否支持BLE feature:
// Use this check to determine whether BLE is supported on the device. Then
// you can selectively disable BLE-related features.
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
四、啟動(dòng)藍(lán)牙:
在使用藍(lán)牙BLE之前,需要確認(rèn)Android設(shè)備是否支持BLE feature(required為false時(shí)),另外要需要確認(rèn)藍(lán)牙是否打開。
如果發(fā)現(xiàn)不支持BLE,則不能使用BLE相關(guān)的功能。如果支持BLE,但是藍(lán)牙沒打開,則需要打開藍(lán)牙。
打開藍(lán)牙的步驟:
1、獲取BluetoothAdapter
BluetoothAdapter是Android系統(tǒng)中所有藍(lán)牙操作都需要的,它對(duì)應(yīng)本地Android設(shè)備的藍(lán)牙模塊,在整個(gè)系統(tǒng)中BluetoothAdapter是單例的。當(dāng)你獲取到它的示例之后,就能進(jìn)行相關(guān)的藍(lán)牙操作了。
獲取BluetoothAdapter代碼示例如下:
// Initializes Bluetooth adapter.
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
注:這里通過getSystemService獲取BluetoothManager,再通過BluetoothManager獲取BluetoothAdapter。BluetoothManager在Android4.3以上支持(API level 18)。
2、判斷是否支持藍(lán)牙,并打開藍(lán)牙
獲取到BluetoothAdapter之后,還需要判斷是否支持藍(lán)牙,以及藍(lán)牙是否打開。
如果沒打開,需要讓用戶打開藍(lán)牙:
private BluetoothAdapter mBluetoothAdapter;
...
// Ensures Bluetooth is available on the device and it is enabled. If not,
// displays a dialog requesting user permission to enable Bluetooth.
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
五、搜索BLE設(shè)備:
由于搜索需要盡量減少功耗,因此在實(shí)際使用時(shí)需要注意:
1、當(dāng)找到對(duì)應(yīng)的設(shè)備后,立即停止掃描;
2、不要循環(huán)搜索設(shè)備,為每次搜索設(shè)置適合的時(shí)間限制。避免設(shè)備不在可用范圍的時(shí)候持續(xù)不停掃描,消耗電量。
搜索的示例代碼如下:
/**
* Activity for scanning and displaying available BLE devices.
*/
public class DeviceScanActivity extends ListActivity {
private BluetoothAdapter mBluetoothAdapter;
private boolean mScanning;
private Handler mHandler;
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;
...
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
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);
}
...
}
...
}
其中UUID數(shù)組指定你的應(yīng)用程序所支持的GATT Services的UUID。
private LeDeviceListAdapter mLeDeviceListAdapter;
...
// Device scan callback.
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();
}
});
}
};
注意:搜索時(shí),你只能搜索傳統(tǒng)藍(lán)牙設(shè)備或者BLE設(shè)備,兩者完全獨(dú)立,不可同時(shí)被搜索。
六、連接GATT Server:
兩個(gè)設(shè)備通過BLE通信,首先需要建立GATT連接。這里我們講的是Android設(shè)備作為client端,連接GATT Server。
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
BluetoothGatt常規(guī)用到的幾個(gè)操作示例:
connect() :連接遠(yuǎn)程設(shè)備。
discoverServices() : 搜索連接設(shè)備所支持的service。
disconnect():斷開與遠(yuǎn)程設(shè)備的GATT連接。
close():關(guān)閉GATT Client端。
readCharacteristic(characteristic) :讀取指定的characteristic。
setCharacteristicNotification(characteristic, enabled) :設(shè)置當(dāng)指定characteristic值變化時(shí),發(fā)出通知。
getServices() :獲取遠(yuǎn)程設(shè)備所支持的services。
等等。
注:
1、某些函數(shù)調(diào)用之間存在先后關(guān)系。例如首先需要connect上才能discoverServices。
2、一些函數(shù)調(diào)用是異步的,需要得到的值不會(huì)立即返回,而會(huì)在BluetoothGattCallback的回調(diào)函數(shù)中返回。例如discoverServices與onServicesDiscovered回調(diào),readCharacteristic與onCharacteristicRead回調(diào),setCharacteristicNotification與onCharacteristicChanged回調(diào)等。