![]() 一、基本介紹BLE全稱Bluetooth Low Energy即低功耗藍牙。 Android 4.3(API Level 18)開始引入核心功能并提供了相應的 API, 應用程序通過這些 API 掃描藍牙設備、查詢 services、讀寫設備的 characteristics(屬性特征)等操作。 Android BLE 使用的藍牙協(xié)議是 GATT 協(xié)議,有關該協(xié)議的詳細內容可以參見官方文檔。 Service一個低功耗藍牙設備可以定義許多 Service, Service 可以理解為一個功能的集合。設備中每一個不同的 Service 都有一個 128 bit 的 UUID 作為這個 Service 的獨立標志。藍牙核心規(guī)范制定了兩種不同的UUID,一種是基本的UUID,一種是代替基本UUID的16位UUID。所有的藍牙技術聯(lián)盟定義UUID共用了一個基本的UUID: 0x0000xxxx-0000-1000-8000-00805F9B34FB 為了進一步簡化基本UUID,每一個藍牙技術聯(lián)盟定義的屬性有一個唯一的16位UUID,以代替上面的基本UUID的'x’部分。例如,心率測量特性使用0X2A37作為它的16位UUID,因此它完整的128位UUID為: 0x00002A37-0000-1000-8000-00805F9B34FB Characteristic在 Service 下面,又包括了許多的獨立數(shù)據(jù)項,我們把這些獨立的數(shù)據(jù)項稱作 Characteristic。同樣的,每一個 Characteristic 也有一個唯一的 UUID 作為標識符。在 Android 開發(fā)中,建立藍牙連接后,我們說的通過藍牙發(fā)送數(shù)據(jù)給外圍設備就是往這些 Characteristic 中的 Value 字段寫入數(shù)據(jù);外圍設備發(fā)送數(shù)據(jù)給手機就是監(jiān)聽這些 Charateristic 中的 Value 字段有沒有變化,如果發(fā)生了變化,手機的 BLE API 就會收到一個監(jiān)聽的回調。 Android BLE API 簡介BluetoothAdapter BluetoothAdapter 擁有基本的藍牙操作,例如開啟藍牙掃描,使用已知的 MAC 地址 (BluetoothAdapter#getRemoteDevice)實例化一個 BluetoothDevice 用于連接藍牙設備的操作等等。 BluetoothDevice 代表一個遠程藍牙設備。這個類可以讓你連接所代表的藍牙設備或者獲取一些有關它的信息,例如它的名字,地址和綁定狀態(tài)等等。 BluetoothGatt 這個類提供了 Bluetooth GATT 的基本功能。例如重新連接藍牙設備,發(fā)現(xiàn)藍牙設備的 Service 等等。 BluetoothGattService 這一個類通過 BluetoothGatt#getService 獲得,如果當前服務不可見那么將返回一個 null。這一個類對應上面說過的 Service。我們可以通過這個類的 getCharacteristic(UUID uuid) 進一步獲取 Characteristic 實現(xiàn)藍牙數(shù)據(jù)的雙向傳輸。 BluetoothGattCharacteristic 這個類對應上面提到的 Characteristic。通過這個類定義需要往外圍設備寫入的數(shù)據(jù)和讀取外圍設備發(fā)送過來的數(shù)據(jù)。 二、開始使用1.聲明權限,在AndroidManifest.xml里面聲明:<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>在API21(Android5.0)之后還需要加: <uses-feature android:name="android.hardware.location.gps" />在Android6.0以后還需要加: <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>好了,權限聲明完成就到了我們的代碼環(huán)節(jié) 2.初始化BluetoothAdapterprivate BluetoothAdapter mAdapter; BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mAdapter = bluetoothManager.getAdapter(); 3.如果藍牙沒有打開先打開藍牙if (mAdapter == null || !mAdapter.isEnabled()) { // 彈對話框的形式提示用戶開啟藍牙 //startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), 1); mBluetoothAdapter.enable();
} 4.掃描設備掃描設備有兩種,一種是過濾特別的服務掃描,一種是全部掃描 下面代碼演示全部掃描 mAdapter.startLeScan(mLeScanCallback); // 開始掃描 private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { Log.i("掃描到",device.getName()+"rssi"+rssi);}};停止掃描 mAdapter.stopLeScan(mLeScanCallback); // 停止掃描 5.連接GATT服務端連接藍牙設備可以通過 BluetoothDevice#ConnectGatt 方法連接,也可以通過 BluetoothGatt#connect 方法進行重新連接。以下分別是兩個方法的官方說明:
第二個參數(shù)表示是否需要自動連接。如果設置為 true, 表示如果設備斷開了,會不斷的嘗試自動連接。設置為 false 表示只進行一次連接嘗試。 第三個參數(shù)是連接后進行的一系列操作的回調,例如連接和斷開連接的回調,發(fā)現(xiàn)服務的回調,成功寫入數(shù)據(jù),成功讀取數(shù)據(jù)的回調等等。 BluetoothDevice device = mAdapter.getRemoteDevice(address); device.connectGatt(this, false, mGattCallback); private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { } }; 當調用藍牙的連接方法之后,藍牙會異步執(zhí)行藍牙連接的操作,如果連接成功會回調 BluetoothGattCalbackl#onConnectionStateChange 方法。這個方法運行的線程是一個 Binder 線程,所以不建議直接在這個線程處理耗時的任務,因為這可能導致藍牙相關的線程被阻塞。 這一個方法有三個參數(shù),第一個就藍牙設備的 Gatt 服務連接類。 第二個參數(shù)代表是否成功執(zhí)行了連接操作,如果為 BluetoothGatt.GATT_SUCCESS 表示成功執(zhí)行連接操作,第三個參數(shù)才有效,否則說明這次連接嘗試不成功。根據(jù)網(wǎng)上大部分人的說法,這是因為 Android 最多支持連接 6 到 7 個左右的藍牙設備,如果超出了這個數(shù)量就無法再連接了。所以當我們斷開藍牙設備的連接時,還必須調用 BluetoothGatt#close 方法釋放連接資源。 第三個參數(shù)代表當前設備的連接狀態(tài),如果 newState == BluetoothProfile.STATE_CONNECTED 說明設備已經(jīng)連接,可以進行下一步的操作了(發(fā)現(xiàn)藍牙服務,也就是 Service)。當藍牙設備斷開連接時,這一個方法也會被回調,其中的 newState == BluetoothProfile.STATE_DISCONNECTED。 6.發(fā)現(xiàn)服務在成功連接到藍牙設備之后才能進行這一個步驟,調用 BluetoothGatt#discoverService 這一個方法。當這一個方法被調用之后,系統(tǒng)會異步執(zhí)行發(fā)現(xiàn)服務的過程,直到 BluetoothGattCallback#onServicesDiscovered 被系統(tǒng)回調之后,手機設備和藍牙設備才算是真正建立了可通信的連接。 if (newState == BluetoothProfile.STATE_CONNECTED) { mBluetoothGatt.discoverServices(); } 7.讀寫設備讀當我們發(fā)現(xiàn)服務之后就可以通過 BluetoothGatt#getService 獲取 BluetoothGattService,接著通過 BluetoothGattService#getCharactristic 獲取 BluetoothGattCharactristic。 通過 BluetoothGattCharactristic#readCharacteristic 方法可以通知系統(tǒng)去讀取特定的數(shù)據(jù)。如果系統(tǒng)讀取到了藍牙設備發(fā)送過來的數(shù)據(jù)就會調用 BluetoothGattCallback#onCharacteristicRead 方法。 通過 BluetoothGattCharacteristic#getValue 可以讀取到藍牙設備的數(shù)據(jù)。 // 讀取數(shù)據(jù) BluetoothGattService service = mBluetoothGatt.getService("需要讀取的服務"); BluetoothGattCharacteristic characteristic = service.getCharacteristic("需要讀取的特征"); mBluetoothGatt.readCharacteristic(characteristic); /** * BLE終端數(shù)據(jù)被讀的事件 */ @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { Log.i("onCharacteristicRead","讀取的回調"+characteristic.getValue()); } 寫和讀取數(shù)據(jù)一樣,在執(zhí)行寫入數(shù)據(jù)前需要獲取到 BluetoothGattCharactristic。接著執(zhí)行一下步驟:
BluetoothGattService service = mBluetoothGatt.getService("需要寫的服務"); BluetoothGattCharacteristic characteristic = service.getCharacteristic("需要寫的特征"); characteristic.setValue("需要寫入的數(shù)據(jù)"); mBluetoothGatt.writeCharacteristic(characteristic); public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if(!characteristic.getValue().equals("需要寫入的數(shù)據(jù)")) { // 執(zhí)行重發(fā)策略 gatt.writeCharacteristic(characteristic); } } 通知mBluetoothGatt.setCharacteristicNotification(characteristic, true); BluetoothGattDescriptor descriptor = characteristic.getDescriptor( UUID.fromString("")); descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);//或者descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor);然后來的通知就會回調在 public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
} 8.斷開連接當我們連接藍牙設備完成一系列的藍牙操作之后就可以斷開藍牙設備的連接了。通過 BluetoothGatt#disconnect 可以斷開正在連接的藍牙設備。當這一個方法被調用之后,系統(tǒng)會異步回調 BluetoothGattCallback#onConnectionStateChange 方法。通過這個方法的 newState 參數(shù)可以判斷是連接成功還是斷開成功的回調。由于 Android 藍牙連接設備的資源有限,當我們執(zhí)行斷開藍牙操作之后必須執(zhí)行 BluetoothGatt#close 方法釋放資源。需要注意的是通過 BluetoothGatt#close 方法也可以執(zhí)行斷開藍牙的操作,不過 BluetoothGattCallback#onConnectionStateChange 將不會收到任何回調。此時如果執(zhí)行 BluetoothGatt#connect 方法會得到一個藍牙 API 的空指針異常。所以,我們推薦的寫法是當藍牙成功連接之后,通過 BluetoothGatt#disconnect 斷開藍牙的連接,緊接著在 BluetoothGattCallback#onConnectionStateChange 執(zhí)行 BluetoothGatt#close 方法釋放資源。 @Override public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) { Log.d(TAG, "onConnectionStateChange: thread " + Thread.currentThread() + " status " + newState); if (status != BluetoothGatt.GATT_SUCCESS) { String err = "Cannot connect device with error status: " + status; // 當嘗試連接失敗的時候調用 disconnect 方法是不會引起這個方法回調的,所以這里 // 直接回調就可以了。 gatt.close(); Log.e(TAG, err); return; } if (newState == BluetoothProfile.STATE_CONNECTED) { gatt.discoverService(); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { gatt.close(); } } |
|