目錄
[TOC]
Bluetooth Low Energy
- 簡(jiǎn)介:
- 與傳統(tǒng)藍(lán)牙相比,低功耗藍(lán)牙的設(shè)計(jì)對(duì)電量消耗更低。
- 只支持Android 4.3以上的系統(tǒng)版本,即 API Level>=18。
藍(lán)牙開發(fā)對(duì)象
-
BluetoothManager
-
獲取對(duì)象:
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); 注意:BluetoothManager僅在Android4.3以上的系統(tǒng)版本支持,即 API Level>=18。
-
-
BluetoothAdapter
-
獲取對(duì)象:
BluetoothAdapter mBluetoothAdapter = BluetoothManager.getAdapter(); // 或者 BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();- 注意:一個(gè)Android系統(tǒng)只有一個(gè)BluetoothAdapter。
-
方法描述:
- 藍(lán)牙狀態(tài)
- isEnabled()
- 判斷系統(tǒng)藍(lán)牙是否打開
- disable()
- 隱式關(guān)閉系統(tǒng)藍(lán)牙
- enable()
- 隱式打開系統(tǒng)藍(lán)牙
- isEnabled()
- 藍(lán)牙設(shè)備搜索
- startDiscovery()/cancelDiscovery()
- 開始/取消搜索設(shè)備(經(jīng)典藍(lán)牙 和 低功耗藍(lán)牙)
- 執(zhí)行過程(耗時(shí)12秒):
- 系統(tǒng)發(fā)送 BluetoothAdapter.ACTIOIN_DISCOVERY_STARTED 的廣播
- 搜索藍(lán)牙設(shè)備...
- 只要找到一個(gè)設(shè)備就發(fā)送一個(gè) BluetoothDevice.ACTION_FOUND 的廣播
- 從廣播接收器中就可以得到這個(gè)BluetoothDevice對(duì)象
- 系統(tǒng)發(fā)送 BluetoothAdapter.ACTION_FINISHED 的廣播
- startLeScan()/stopLeScan()
- 開始/取消搜索設(shè)備(低功耗藍(lán)牙)
- 注意:
- 在API Level-21以上被標(biāo)記廢棄使用
- startDiscovery()/cancelDiscovery()
- 藍(lán)牙狀態(tài)
-
-
BluetoothLeScanner
-
獲取對(duì)象:
BluetoothLeScanner mBluetoothLeScanner= BluetoothAdapter.getBluetoothLeScanner(); -
方法描述:
- startScan()/stopScan()
- 開始/取消搜索設(shè)備(低功耗藍(lán)牙)
- 注意:
- 在API Level-21以上使用
- startScan()/stopScan()
-
-
BluetoothDevice
- 代表了一個(gè)遠(yuǎn)程的藍(lán)牙設(shè)備, 通過這個(gè)類可以查詢遠(yuǎn)程設(shè)備的物理地址, 名稱, 連接狀態(tài)等信息,
- 獲取對(duì)象:
-
物理地址對(duì)應(yīng)的Device:
BluetoothAdapter.getRemoteDevice(address); -
已經(jīng)配對(duì)的Device集合:
BluetoothAdapter.getBoundedDevices(); -
掃描結(jié)果回調(diào)中的Device:
BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { } };
-
-
BluetoothGatt
- BluetoothGatt繼承BluetoothProfile,
- 通過BluetoothGatt可以連接設(shè)備(connect),發(fā)現(xiàn)服務(wù)(discoverServices),并把相應(yīng)地屬性返回到 BluetoothGattCallback,
- 獲取對(duì)象:
- BluetoothGattCallback回調(diào)方法中獲取。
-
BluetoothProfile
- 一個(gè)通用的規(guī)范,按照這個(gè)規(guī)范來收發(fā)數(shù)據(jù)。
連接成功后,我們首先需要獲得服務(wù),然后是該服務(wù)所包含的特征,最后是特征的描述符。
-
服務(wù)(Service):
- BluetoothGattService
- 一個(gè) Service 可以包含多個(gè) Characteristic,
- 獲取對(duì)象:
-
通過指定的 UUID 從 BluetoothGatt 實(shí)例中獲得:
BluetoothGattService service = BluetoothGatt.getService(UUID.fromString(BLE_SERVICE));
-
- BluetoothGattService
-
特征(Characteristic):
- BluetoothGattCharacteristic
- 一個(gè) Characteristic 包含一個(gè) Value 和多個(gè) Descriptor,
- 相當(dāng)于一個(gè)數(shù)據(jù)類型,它包括一個(gè)value和0~n個(gè)value的描述(Descriptor),
- 獲取對(duì)象:
-
通過指定的 UUID 從 BluetoothGattService 中得到:
BluetoothGattCharacteristic characteristic = BluetoothGattService.getCharacteristic(UUID.fromString(BLE_CHARACTERISTIC))
-
- BluetoothGattCharacteristic
-
描述符(Descriptor):
- BluetoothGattDescriptor
- 一個(gè) Descriptor 包含一個(gè) Value,
- 對(duì) Characteristic 的描述,包括范圍、計(jì)量單位等,
- 獲取對(duì)象:
-
通過指定的 UUID 從 BluetoothGattCharacteristic 對(duì)象中獲得:
List<BluetoothGattDescriptor> descriptorList = BluetoothGattCharacteristic.getDescriptors(); // 或者 BluetoothGattDescriptor descriptor = BluetoothGattCharacteristic.getDescriptor(UUID.fromString("BLE_DESCRIPTOR"));
-
- BluetoothGattDescriptor
低功耗藍(lán)牙開發(fā)基本流程
-
申請(qǐng)權(quán)限
-
基礎(chǔ)權(quán)限:
<!-- 執(zhí)行所有的藍(lán)牙通信,如請(qǐng)求連接,接受連接和傳輸數(shù)據(jù) --> <uses-permission android:name="android.permission.BLUETOOTH"/> <!-- 初始化設(shè)備發(fā)現(xiàn)或者操縱藍(lán)牙設(shè)置 --> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> -
注意:
- 如果應(yīng)用僅支持低功耗藍(lán)牙
-
在 AndroidManifest.xml 添加以下聲明:
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/> -
動(dòng)態(tài)判斷:
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, "該設(shè)備不支持低功耗藍(lán)牙", Toast.LENGTH_SHORT).show(); }
-
-
在 Android 6.0 及以上的系統(tǒng)版本,需動(dòng)態(tài)申請(qǐng)位置權(quán)限。
如果應(yīng)用沒有位置權(quán)限,藍(lán)牙掃描功能不能使用(其它藍(lán)牙操作例如連接藍(lán)牙設(shè)備和寫入數(shù)據(jù)不受影響)。-
在 AndroidManifest.xml 添加以下聲明:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> 或 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> -
動(dòng)態(tài)申請(qǐng):
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION); } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode) { case PERMISSION_REQUEST_COARSE_LOCATION: if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 權(quán)限申請(qǐng)成功,處理業(yè)務(wù)邏輯 } break; default: break; } }
-
- 如果應(yīng)用僅支持低功耗藍(lán)牙
-
-
是否支持藍(lán)牙
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); if (bluetoothManager.getAdapter() == null) { Toast.makeText(Context, "沒有發(fā)現(xiàn)藍(lán)牙模塊", Toast.LENGTH_SHORT).show(); } -
開啟/關(guān)閉藍(lán)牙
-
檢查藍(lán)牙是否開啟
BluetoothAdapter.isEnabled(); -
開啟
-
隱式開啟
BluetoothAdapter.enable();- 注意:
-
需要注冊(cè)權(quán)限:
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> 在 Android 6.0 及以上的系統(tǒng)版本,隱式開啟依舊會(huì)提示用戶。
-
- 注意:
-
顯示開啟
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUESTCODE_BLUETOOTH);-
確認(rèn)結(jié)果:
-
方法一:注冊(cè)廣播
IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//藍(lán)牙狀態(tài) intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);// 掃描開始 intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);// 掃描結(jié)束 registerReceiver(new BluetoothReceiver(), intentFilter); private class BluetoothReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) { switch (BluetoothAdapter.getDefaultAdapter().getState()) { case BluetoothAdapter.STATE_ON:// 打開 break; case BluetoothAdapter.STATE_OFF:// 關(guān)閉 break; case BluetoothAdapter.STATE_TURNING_OFF:// 藍(lán)牙處于關(guān)閉過程中 break; case BluetoothAdapter.STATE_TURNING_ON:// 藍(lán)牙處于打開過程中 break; default: break; } } } } -
方法二:重寫onActivityResult()
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) {//藍(lán)牙開啟成功 switch (requestCode) { case REQUESTCODE_BLUETOOTH: break; default: break; } } else if (resultCode == RESULT_CANCELED) {//藍(lán)牙開啟失敗 ToastUtils.showShort("藍(lán)牙開啟失敗,請(qǐng)手動(dòng)開啟藍(lán)牙"); } }
-
-
前往系統(tǒng)設(shè)置界面
startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
-
-
-
關(guān)閉
-
隱式關(guān)閉
BluetoothAdapter.disable(); -
前往系統(tǒng)設(shè)置界面
startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
-
-
-
掃描藍(lán)牙設(shè)備
- 開啟/關(guān)閉掃描設(shè)備
-
開啟
-
掃描全部藍(lán)牙設(shè)備
BluetoothAdapter.startLeScan(BluetoothAdapter.LeScanCallback callback) -
只掃描含有特定 UUID Service 的藍(lán)牙設(shè)備
BluetoothAdapter.startLeScan(UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback)
-
-
關(guān)閉
BluetoothAdapter.stopLeScan(BluetoothAdapter.LeScanCallback callback)
-
- 開啟/關(guān)閉掃描設(shè)備
-
連接設(shè)備
BluetoothGatt BluetoothDevice.connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback) {}- autoConnect:表示是否需要自動(dòng)連接。
- 設(shè)置為 true 表示如果設(shè)備斷開了,會(huì)不斷的嘗試自動(dòng)連接。
- 設(shè)置為 false 表示只進(jìn)行一次連接嘗試。
- BluetoothGattCallback:表示連接后進(jìn)行的一系列操作的回調(diào),共計(jì)9個(gè)回調(diào)方法,以下列舉常用的5個(gè)。
-
連接狀態(tài)變化
void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {} -
執(zhí)行BluetoothGatt.discoverServices();后回調(diào),發(fā)現(xiàn)服務(wù)
void onServicesDiscovered(BluetoothGatt gatt, int status) {} -
執(zhí)行BluetoothGatt.writeCharacteristic();后回調(diào),寫入數(shù)據(jù)執(zhí)行結(jié)果
void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {} -
執(zhí)行BluetoothGatt.setCharacteristicNotification();后回調(diào),Characteristic值發(fā)生改變
void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {} -
執(zhí)行BluetoothGatt.writeDescriptor();后回調(diào)
void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {}
-
- autoConnect:表示是否需要自動(dòng)連接。
-
發(fā)現(xiàn)服務(wù)
-
在onConnectionStateChange()中,判斷連接狀態(tài),連接成功,搜索連接設(shè)備所支持的service
@Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); if (newState == BluetoothGatt.STATE_CONNECTED) {//連接成功 gatt.discoverServices();//搜索連接設(shè)備所支持的service } else if (newState == BluetoothGatt.STATE_DISCONNECTED) {//連接斷開 } } -
discoverServices()被執(zhí)行后,在onServicesDiscovered()中,獲取指定UUID Service,存下BluetoothGatt對(duì)象
@Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); BluetoothGattService service = gatt.getService(UUID.fromString(BLE_SERVICE)); if (service != null) { mBluetoothGatt = gatt; } }
-
-
讀寫數(shù)據(jù)
- 寫:
獲取指定 UUID Serivce 的 BluetoothGattService 對(duì)象,
從而得到指定 UUID Characteristic 的 BluetoothGattCharacteristic 對(duì)象,將數(shù)據(jù)設(shè)置進(jìn)去,
-
最后用 BluetoothGatt 對(duì)象向藍(lán)牙設(shè)備寫入數(shù)據(jù)
public void writeCharacteristic() { byte[] data = {0x00};//封包數(shù)據(jù),根據(jù)硬件協(xié)議填寫 BluetoothGattCharacteristic characteristic = mBluetoothGatt.getService(UUID.fromString(BLE_SERVICE)).getCharacteristic(UUID.fromString(BLE_WRITE)); characteristic.setValue(data);//設(shè)置數(shù)據(jù) mBluetoothGatt.writeCharacteristic(characteristic);//寫入設(shè)備 }
- 讀:
在 writeCharacteristic() 調(diào)用后,
會(huì)走 onCharacteristicChanged() 回調(diào),
在 BluetoothGattCharacteristic 對(duì)象中獲取藍(lán)牙設(shè)備傳回的數(shù)據(jù),
-
通過藍(lán)牙硬件協(xié)議判斷并做出相應(yīng)處理。
@Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); byte[] value = characteristic.getValue(); switch (value[0]) {//判斷協(xié)議,對(duì)應(yīng)處理 case 0x00: break; default: break; } }
- 寫:
-
斷開連接
BluetoothGatt.disconnect(); BluetoothGatt.close();