1. 程式人生 > >android 4.0 BLE開發官方文件介紹

android 4.0 BLE開發官方文件介紹

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

關鍵術語和概念

  • Generic Attribute Profile(GATT)—GATT配置檔案是一個通用規範,用於在BLE鏈路上傳送和接收被稱為“屬性”的資料塊。目前所有的BLE應用都基於GATT。 藍芽SIG規定了許多低功耗裝置的配置檔案。配置檔案是裝置如何在特定的應用程式中工作的規格說明。注意一個裝置可以實現多個配置檔案。例如,一個裝置可能包括心率監測儀和電量檢測。
  • Attribute Protocol(ATT)—GATT在ATT協議基礎上建立,也被稱為GATT/ATT。ATT對在BLE裝置上執行進行了優化,為此,它使用了儘可能少的位元組。每個屬性通過一個唯一的的統一識別符號(UUID)來標識,每個String型別UUID使用128 bit標準格式。屬性通過ATT被格式化為characteristics和services。
  • Characteristic 一個characteristic包括一個單一變數和0-n個用來描述characteristic變數的descriptor,characteristic可以被認為是一個型別,類似於類。
  • Descriptor Descriptor用來描述characteristic變數的屬性。例如,一個descriptor可以規定一個可讀的描述,或者一個characteristic變數可接受的範圍,或者一個characteristic變數特定的測量單位。
  • Service service是characteristic的集合。例如,你可能有一個叫“Heart Rate Monitor(心率監測儀)”的service,它包括了很多characteristics,如“heart rate measurement(心率測量)”等。你可以在bluetooth.org 找到一個目前支援的基於GATT的配置檔案和服務列表。

角色和責任

以下是Android裝置與BLE裝置互動時的角色和責任:

  • 中央 VS 外圍裝置。 適用於BLE連線本身。中央裝置掃描,尋找廣播;外圍裝置發出廣播。
  • GATT 服務端 VS GATT 客戶端。決定了兩個裝置在建立連線後如何互相交流。

為了方便理解,想象你有一個Android手機和一個用於活動跟蹤BLE裝置,手機支援中央角色,活動跟蹤器支援外圍(為了建立BLE連線你需要注意兩件事,只支援外圍裝置的兩方或者只支援中央裝置的兩方不能互相通訊)。

當手機和運動追蹤器建立連線後,他們開始向另一方傳輸GATT資料。哪一方作為伺服器取決於他們傳輸資料的種類。例如,如果運動追蹤器想向手機報告感測器資料,運動追蹤器是服務端。如果運動追蹤器更新來自手機的資料,手機會作為服務端。

在這份文件的例子中,android app(執行在android裝置上)作為GATT客戶端。app從gatt服務端獲得資料,gatt服務端即支援Heart Rate Profile(心率配置)的BLE心率監測儀。但是你可以自己設計android app去扮演GATT服務端角色。更多資訊見BluetoothGattServer

BLE許可權

為了在app中使用藍芽功能,必須宣告藍芽許可權BLUETOOTH。利用這個許可權去執行藍芽通訊,例如請求連線、接受連線、和傳輸資料。

在你的app manifest檔案中宣告藍芽許可權。

12 <uses-permission android:name="android.permission.BLUETOOTH"/><uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

如果想宣告你的app只為具有BLE的裝置提供,在manifest檔案中包括:

1 <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

但是如果想讓你的app提供給那些不支援BLE的裝置,需要在manifest中包括上面程式碼並設定required="false",然後在執行時可以通過使用PackageManager.hasSystemFeature()確定BLE的可用性。

12345 // 使用此檢查確定BLE是否支援在裝置上,然後你可以有選擇性禁用BLE相關的功能if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show(); finish();}

設定BLE

你的app能與BLE通訊之前,你需要確認裝置是否支援BLE,如果支援,確認已經啟用。注意如果<uses-feature.../>設定為false,這個檢查才是必需的。

如果不支援BLE,那麼你應該適當地禁用部分BLE功能。如果支援BLE但被禁用,你可以無需離開應用程式而要求使用者啟動藍芽。使用BluetoothAdapter兩步完成該設定。

  1. 所有的藍芽活動都需要藍芽介面卡。BluetoothAdapter代表裝置本身的藍芽介面卡(藍芽無線)。整個系統只有一個藍芽介面卡,而且你的app使用它與系統互動。下面的程式碼片段顯示瞭如何得到介面卡。注意該方法使用getSystemService()]返回BluetoothManager,然後將其用於獲取介面卡的一個例項。Android 4.3(API 18)引入BluetoothManager。

1234 // 初始化藍芽介面卡final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);mBluetoothAdapter = bluetoothManager.getAdapter();
  1. 開啟藍芽

    接下來,你需要確認藍芽是否開啟。呼叫isEnabled())去檢測藍芽當前是否開啟。如果該方法返回false,藍芽被禁用。下面的程式碼檢查藍芽是否開啟,如果沒有開啟,將顯示錯誤提示使用者去設定開啟藍芽。

12345 // 確保藍芽在裝置上可以開啟if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);}

發現BLE裝置

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

  • 只要找到所需的裝置,停止掃描。
  • 不要在迴圈裡掃描,並且對掃描設定時間限制。以前可用的裝置可能已經移出範圍,繼續掃描消耗電池電量。

下面程式碼顯示瞭如何開始和停止一個掃描:

12345678910111213141516171819202122232425262728293031323334 /** * 掃描和顯示可以提供的藍芽裝置. */public class DeviceScanActivity extends ListActivity { private BluetoothAdapter mBluetoothAdapter; private boolean mScanning; private Handler mHandler; // 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); } ... }...}

作為BLE掃描結果的介面,下面是BluetoothAdapter.LeScanCallback的實現。

123456789101112131415161718 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(); } }); }};

注意:只能掃描BLE裝置或者掃描傳統藍芽裝置,不能同時掃描BLE和傳統藍芽裝置。

連線到GATT服務端

與一個BLE裝置互動的第一步就是連線它——更具體的,連線到BLE裝置上的GATT服務端。為了連線到BLE裝置上的GATT服務端,需要使用connectGatt( )方法。這個方法需要三個引數:一個Context物件,自動連線(boolean值,表示只要BLE裝置可用是否自動連線到它),和BluetoothGattCallback呼叫。

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

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

在這個例子中,這個BLE APP提供了一個activity(DeviceControlActivity)來連線,顯示資料,顯示該裝置支援的GATT services和characteristics。根據使用者的輸入,這個activity與BluetoothLeService通訊,通過Android BLE API實現與BLE裝置互動。

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475 //通過BLE API服務端與BLE裝置互動public class BluetoothLeService extends Service { private final static String TAG = BluetoothLeService.class.getSimpleName(); private BluetoothManager mBluetoothManager; //藍芽管理器 private BluetoothAdapter mBluetoothAdapter; //藍芽介面卡 private String mBluetoothDeviceAddress; //藍芽裝置地址 private BluetoothGatt mBluetoothGatt; private int mConnectionState = STATE_DISCONNECTED; private static final int STATE_DISCONNECTED = 0; //裝置無法連線 private static final int STATE_CONNECTING = 1; //裝置正在連線狀態 private static final int STATE_CONNECTED = 2; //裝置連線完畢 public final static String ACTION_GATT_CONNECTED = "com.example.bluetooth.le.ACTION_GATT_CONNECTED"; public final static String ACTION_GATT_DISCONNECTED = "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"; public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED"; public final static String ACTION_DATA_AVAILABLE = "com.example.bluetooth.le.ACTION_DATA_AVAILABLE"; public final static String EXTRA_DATA = "com.example.bluetooth.le.EXTRA_DATA"; public final static UUID UUID_HEART_RATE_MEASUREMENT = UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT); //通過BLE API的不同型別的回撥方法 private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {//當連線狀態發生改變 String intentAction; if (newState == BluetoothProfile.STATE_CONNECTED) {//當藍芽裝置已經連線 intentAction = ACTION_GATT_CONNECTED; mConnectionState = STATE_CONNECTED; broadcastUpdate(intentAction); Log.i(TAG, "Connected to GATT server."); Log.i(TAG, "Attempting to start service discovery:" + mBluetoothGatt.discoverServices()); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {//當裝置無法連線 intentAction = ACTION_GATT_DISCONNECTED; mConnectionState = STATE_DISCONNECTED; Log.i(TAG, "Disconnected from GATT server."); broadcastUpdate(intentAction); } } @Override // 發現新服務端 public void onServicesDiscovered(BluetoothGatt gatt, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED); } else { Log.w(TAG, "onServicesDiscovered received: " + status); } } @Override // 讀寫特性 public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); } } ... };...}

當一個特定的回撥被觸發的時候,它會呼叫相應的broadcastUpdate()輔助方法並且傳遞給它一個action。注意在該部分中的資料解析按照藍芽心率測量配置檔案規格進行。

12345678910111213141516171819202122232425262728293031323334353637 private void broadcastUpdate(final String action) { final Intent intent = new Intent(action); sendBroadcast(intent);}private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) { final Intent intent = new Intent(action); // 這是心率測量配置檔案。 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 { // 對於所有其他的配置檔案,用十六進位制格式寫資料 final byte[] data = characteristic.getValue(); if (data != null && data.length > 0) { final StringBuilder stringBuilder = new StringBuilder(data.length); for(byte byteChar : data) stringBuilder.append(String.format("%02X ", byteChar)); intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString()); } } sendBroadcast(intent);}

返回DeviceControlActivity, 這些事件由一個BroadcastReceiver來處理:

123456789101112131415161718192021222324252627 // 通過服務控制不同的事件// ACTION_GATT_CONNECTED: 連線到GATT服務端// ACTION_GATT_DISCONNECTED: 未連線GATT服務端.// ACTION_GATT_SERVICES_DISCOVERED: 未發現GATT服務.// ACTION_DATA_AVAILABLE: 接受來自裝置的資料,可以通過讀或通知操作獲得。private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) { mConnected = true; updateConnectionState(R.string.connected); invalidateOptionsMenu(); } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) { mConnected = false; updateConnectionState(R.string.disconnected); invalidateOptionsMenu(); clearUI(); } else if (BluetoothLeService. ACTION_GATT_SERVICES_DISCOVERED.equals(action)) { // 在使用者介面上展示所有的services and characteristics displayGattServices(mBluetoothLeService.getSupportedGattServices()); } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) { displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA)); } }};

讀取BLE變數

你的android app完成與GATT服務端連線和發現services後,就可以讀寫支援的屬性。例如,這段程式碼通過服務端的services和 characteristics迭代,並且將它們顯示在UI上。

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455 public class DeviceControlActivity extends Activity { ... // 演示如何遍歷支援GATT Services/Characteristics // 這個例子中,我們填充繫結到UI的ExpandableListView上的資料結構 private void displayGattServices(List<BluetoothGattService> gattServices) { if (gattServices == null) return; String uuid = null; String unknownServiceString = getResources(). getString(R.string.unknown_service); String unknownCharaString = getResources(). getString(R.string.unknown_characteristic); ArrayList<HashMap<String, String>> gattServiceData = new ArrayList<HashMap<String, String>>(); ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData = new ArrayList<ArrayList<HashMap<String, String>>>(); mGattCharacteristics = new ArrayList<ArrayList<BluetoothGattCharacteristic>>(); // 迴圈可用的GATT Services. for (BluetoothGattService gattService : gattServices) { HashMap<String, String> currentServiceData = new HashMap<String, String>(); uuid = gattService.getUuid().toString(); currentServiceData.put( LIST_NAME, SampleGattAttributes. lookup(uuid, unknownServiceString)); currentServiceData.put(LIST_UUID, uuid); gattServiceData.add(currentServiceData); ArrayList<HashMap<String, String>> gattCharacteristicGroupData = new ArrayList<HashMap<String, String>>(); List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics(); ArrayList<BluetoothGattCharacteristic> charas = new ArrayList<BluetoothGattCharacteristic>(); // 迴圈可用的Characteristics. for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) { charas.add(gattCharacteristic); HashMap<String, String> currentCharaData = new HashMap<String, String>(); uuid = gattCharacteristic.getUuid().toString(); currentCharaData.put( LIST_NAME, SampleGattAttributes.lookup(uuid, unknownCharaString)); currentCharaData.put(LIST_UUID, uuid); gattCharacteristicGroupData.add(currentCharaData); } mGattCharacteristics.add(charas); gattCharacteristicData.add(gattCharacteristicGroupData); } ... }...}

接收GATT通知

當裝置上的特性改變時會通知BLE應用程式。這段程式碼顯示瞭如何使用setCharacteristicNotification( )給一個特性設定通知。

1234567891011 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);
123456 @Override// 廣播更新public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);}

關閉客戶端App

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

1234567 public void close() { if (mBluetoothGatt == null) { return; } mBluetoothGatt.close(); mBluetoothGatt = null;}