低功耗藍牙BLE

Google在android 4.3(API Level 18)的android版本中引入了低功耗藍牙BLE核心API。低功耗藍牙BLE也就是我們經(jīng)常說的藍牙4.0, 該技術(shù)擁有極低的運行和待機功耗,使用一粒紐扣電池甚至可連續(xù)工作數(shù)年之久。先不講藍牙協(xié)議與藍牙模塊一些類的作用與之間的關(guān)系,本章僅僅記錄android Ble開發(fā)中的掃描模塊及其一些細節(jié)。

一、聲明藍牙權(quán)限和定位權(quán)限

復(fù)制代碼

????由于藍牙掃描需要用到模糊定位權(quán)限,所以android6.0之后,除了在 AndroidManifest.xml中 申明權(quán)限之外,還需要動態(tài)申請定位權(quán)限,才可進行藍牙掃描,否則不會掃描到任何Ble設(shè)備。

二、中心設(shè)備與外圍設(shè)備

????Ble開發(fā)中,存在著兩個角色:中心設(shè)備角色和外圍設(shè)備角色。粗略了解下:

外圍設(shè)備:一般指非常小或者低功耗設(shè)備,更強大的中心設(shè)備可以連接外圍設(shè)備為中心設(shè)備提供數(shù)據(jù)。外設(shè)會不停的向外廣播,讓中心設(shè)備知道它的存在。 例如小米手環(huán)。

中心設(shè)備:可以掃描并連接多個外圍設(shè)備,從外設(shè)中獲取信息。

外圍設(shè)備會設(shè)定一個廣播間隔,每個廣播間隔中,都會發(fā)送自己的廣播數(shù)據(jù)。廣播間隔越長,越省電。一個沒有被連接的Ble外設(shè)會不斷發(fā)送廣播數(shù)據(jù),這時可以被多個中心設(shè)備發(fā)現(xiàn)。一旦外設(shè)被連接,則會馬上停止廣播。

android 4.3 時引入的Ble核心Api只支持android手機作為中心設(shè)備角色,當android 5.0 更新Api后,android手機支持充當作為外設(shè)角色和中心角色。即 android 5.0 引入了外設(shè)角色的Api,同時也更新了部分中心角色的Api。比如:中心角色中,更新了藍牙掃描的Api。

三、打開藍牙

//初始化ble設(shè)配器BluetoothManager manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);BluetoothAdapter mBluetoothAdapter = manager.getAdapter();//判斷藍牙是否開啟,如果關(guān)閉則請求打開藍牙if(mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {? ? //方式一:請求打開藍牙? ? Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);? ? startActivityForResult(intent, 1);? ? //方式二:半靜默打開藍牙? ? //低版本android會靜默打開藍牙,高版本android會請求打開藍牙? ? //mBluetoothAdapter.enable();}復(fù)制代碼

????mBluetoothAdapter.isEnabled()判斷當前藍牙是否打開,如果藍牙處于打開狀態(tài)返回true。

????同時可以在activity層通過廣播監(jiān)聽藍牙的關(guān)閉與開啟,進行自己的邏輯處理:

newBroadcastReceiver() {? ? @Override? ? public void onReceive(Context context, Intent intent) {? ? ? ? //獲取藍牙廣播? 本地藍牙適配器的狀態(tài)改變時觸發(fā)? ? ? ? String action = intent.getAction();if(action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {? ? ? ? ? ? //獲取藍牙廣播中的藍牙新狀態(tài)? ? ? ? ? ? int blueNewState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0);? ? ? ? ? ? //獲取藍牙廣播中的藍牙舊狀態(tài)? ? ? ? ? ? int blueOldState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0);? ? ? ? ? ? switch (blueNewState) {? ? ? ? ? ? ? ? //正在打開藍牙caseBluetoothAdapter.STATE_TURNING_ON:break;? ? ? ? ? ? ? ? ? ? //藍牙已打開caseBluetoothAdapter.STATE_ON:break;? ? ? ? ? ? ? ? ? ? //正在關(guān)閉藍牙caseBluetoothAdapter.STATE_TURNING_OFF:break;? ? ? ? ? ? ? ? ? ? //藍牙已關(guān)閉caseBluetoothAdapter.STATE_OFF:break;? ? ? ? ? ? }? ? ? ? }? ? }};復(fù)制代碼

四、掃描

android 4.3 掃描

在android 4.3 和 android 4.4進行藍牙掃描中,可使用BluetoothAdapter.startLeScan(BluetoothAdapter.LeScanCallback)進行藍牙掃描。

//開始掃描mBluetoothAdapter.startLeScan(mLeScanCallback);//停止掃描mBluetoothAdapter.stopLeScan(mLeScanCallback);復(fù)制代碼

android 5.0以上 掃描

????在 android 5.0之后的版本(包括 5.0)建議使用新的Api進行藍牙掃描:

BluetoothLeScanner.startScan(ScanCallback)或

BluetoothLeScanner.startScan(List, ScanSettings, ScanCallback)。

//獲取 5.0 的掃描類實例mBLEScanner = mBluetoothAdapter.getBluetoothLeScanner();//開始掃描//可設(shè)置過濾條件,在第一個參數(shù)傳入,但一般不設(shè)置過濾。mBLEScanner.startScan(null,mScanSettings,mScanCallback);//停止掃描mBLEScanner.stopScan(mScanCallback);復(fù)制代碼

藍牙掃描示例:

//如果沒打開藍牙,不進行掃描操作,或請求打開藍牙。if(!mBluetoothAdapter.isEnabled()) {return;} //處于未掃描的狀態(tài)if(!mScanning){? ? //android 5.0后if(android.os.Build.VERSION.SDK_INT >= 21) {? ? ? ? //標記當前的為掃描狀態(tài)? ? ? ? mScanning =true;? ? ? ? //獲取5.0新添的掃描類if(mBLEScanner == null){? ? ? ? ? ? //mBLEScanner是5.0新添加的掃描類,通過BluetoothAdapter實例獲取。? ? ? ? ? ? mBLEScanner = mBluetoothAdapter.getBluetoothLeScanner();? ? ? ? }? ? ? ? //開始掃描? ? ? ? //mScanSettings是ScanSettings實例,mScanCallback是ScanCallback實例,后面進行講解。? ? ? ? mBLEScanner.startScan(null,mScanSettings,mScanCallback);? ? }else{? ? ? ? //標記當前的為掃描狀態(tài)? ? ? ? mScanning =true;? ? ? ? //5.0以下? 開始掃描? ? ? ? //mLeScanCallback是BluetoothAdapter.LeScanCallback實例? ? ? ? mBluetoothAdapter.startLeScan(mLeScanCallback);? ? }? ? //設(shè)置結(jié)束掃描? ? mHandler.postDelayed(newRunnable() {? ? ? ? @Override? ? ? ? public voidrun() {? ? ? ? ? ? //停止掃描設(shè)備if(android.os.Build.VERSION.SDK_INT >= 21) {? ? ? ? ? ? ? ? //標記當前的為未掃描狀態(tài)? ? ? ? ? ? ? ? mScanning =false;? ? ? ? ? ? ? ? mBLEScanner.stopScan(mScanCallback);? ? ? ? ? ? }else{? ? ? ? ? ? ? ? //標記當前的為未掃描狀態(tài)? ? ? ? ? ? ? ? mScanning =false;? ? ? ? ? ? ? ? //5.0以下? 停止掃描? ? ? ? ? ? ? ? mBluetoothAdapter.stopLeScan(mLeScanCallback);? ? ? ? ? ? }? ? ? ? }? ? },SCAN_TIME);}復(fù)制代碼

????掃描代碼如上述所示,當掃描到所需要的設(shè)備的時候,就要手動馬上停止藍牙掃描,因為藍牙掃描是耗電操作。

注意事項:

android 6.0 以上需要獲取到定位權(quán)限。否則會爆如下運行時異常:

android 7.0 后不能在30秒內(nèi)掃描和停止超過5次。(官網(wǎng)沒特意說明,可自行測試,設(shè)置掃描時長為3秒,連續(xù)掃描10次,穩(wěn)定復(fù)現(xiàn)5次后不能掃描到任何設(shè)備。android 藍牙模塊會打印當前應(yīng)用掃描太頻繁的log日志,并在android 5.0 的ScanCallback回調(diào)中觸發(fā)onScanFailed(int),返回錯誤碼:ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED,表示app無法注冊,無法開始掃描)。

五、掃描回調(diào)

android 4.3 掃描回調(diào):LeScanCallback

1、android 4.3 的掃描回調(diào)接口BluetoothAdapter.LeScanCallback:

回調(diào)接口中只有一個回調(diào)函數(shù)onLeScan,掃描到的設(shè)備會通過該方法返回。

參數(shù):

BluetoothDevice 掃描到的設(shè)備實例,可從實例中獲取到相應(yīng)的信息。如:名稱,mac地址

rssi 可理解成設(shè)備的信號值。該數(shù)值是一個負數(shù),越大則信號越強。

scanRecord 遠程設(shè)備提供的廣播數(shù)據(jù)的內(nèi)容。

//5.0以下mLeScanCallback = new BluetoothAdapter.LeScanCallback() {? ? @Override? ? public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {? ? ? ? //對掃描到的設(shè)備進行操作。如:獲取設(shè)備信息。? ? ? ? ? ? }};復(fù)制代碼

獲取BluetoothDevice中的信息:

可以從中獲取到設(shè)備的mac地址,設(shè)備名稱,綁定狀態(tài)和設(shè)備類型等信息,并作相應(yīng)的保存。

mac可用于再創(chuàng)建BluetoothDevice對象進行g(shù)att連接。

綁定狀態(tài):

BOND_NONE:數(shù)值 10

表示遠程設(shè)備未綁定,沒有共享鏈接密鑰,因此通信(如果允許的話)將是未經(jīng)身份驗證和未加密的。(掃描到未綁定的小米手環(huán))

????BOND_BONDING:數(shù)值 11 ????表示正在與遠程設(shè)備進行綁定;

????BOND_BONDED:數(shù)值 12 ????表示遠程設(shè)備已綁定,遠程設(shè)備本地存儲共享連接的密鑰,因此可以對通信進行身份驗證和加密。(掃描到已綁定的小米手環(huán))

設(shè)備類型:一般是2,表示LE設(shè)備

注:回調(diào)函數(shù)中盡量不要做耗時操作!

android 5.0 掃描回調(diào):ScanCallback

mScanCallback = newScanCallback() {? ? //當一個藍牙ble廣播被發(fā)現(xiàn)時回調(diào)? ? @Override? ? public void onScanResult(int callbackType, ScanResult result) {? ? ? ? super.onScanResult(callbackType, result);? ? ? ? //掃描類型有開始掃描時傳入的ScanSettings相關(guān)? ? ? ? //對掃描到的設(shè)備進行操作。如:獲取設(shè)備信息。? ? ? ? ? ? }? ? //批量返回掃描結(jié)果? ? //@param results 以前掃描到的掃描結(jié)果列表。? ? @Override? ? public void onBatchScanResults(List results) {? ? ? ? super.onBatchScanResults(results);? ? ? ? ? ? }? ? //當掃描不能開啟時回調(diào)? ? @Override? ? public void onScanFailed(int errorCode) {? ? ? ? super.onScanFailed(errorCode);? ? ? ? //掃描太頻繁會返回ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED,表示app無法注冊,無法開始掃描。? ? ? ? }};復(fù)制代碼

ScanCallback掃描回調(diào)存在三個回調(diào)函數(shù):

onScanResult(int,ScanResult):?

類似于BluetoothAdapter.LeScanCallback中的onLeScan(),可在ScanResult實例中獲取到BluetoothDevice藍牙設(shè)備對象,rssi信號值等信息,一般都是在該函數(shù)中回調(diào)獲取掃描到藍牙設(shè)備和信號值,在本函數(shù)中執(zhí)行onLeScan()中相同的邏輯處理即可。

onBatchScanResults(List) 批量返回掃描結(jié)果

onScanFailed(int) 掃描失敗返回錯誤碼

一般藍牙設(shè)備對象都是通過onScanResult(int,ScanResult)返回,而不會在onBatchScanResults(List)方法中返回,除非手機支持批量掃描模式并且開啟了批量掃描模式。批處理的開啟請查看ScanSettings。

六、ScanSettings:

????????ScanSettings實例對象是通過ScanSettings.Builder構(gòu)建的。通過Builder對象為ScanSettings實例設(shè)置掃描模式、回調(diào)類型、匹配模式等參數(shù),用于配置android 5.0 的掃描參數(shù)。

//創(chuàng)建ScanSettings的build對象用于設(shè)置參數(shù)ScanSettings.Builder builder = new ScanSettings.Builder()? ? //設(shè)置高功耗模式? ? .setScanMode(SCAN_MODE_LOW_LATENCY);? ? //android 6.0添加設(shè)置回調(diào)類型、匹配模式等if(android.os.Build.VERSION.SDK_INT >= 23) {? ? ? ? //定義回調(diào)類型? ? ? ? builder.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)? ? ? ? //設(shè)置藍牙LE掃描濾波器硬件匹配的匹配模式? ? ? ? builder.setMatchMode(ScanSettings.MATCH_MODE_STICKY);? ? }//芯片組支持批處理芯片上的掃描if(bluetoothadapter.isOffloadedScanBatchingSupported()) {? ? //設(shè)置藍牙LE掃描的報告延遲的時間(以毫秒為單位)? ? //設(shè)置為0以立即通知結(jié)果? ? builder.setReportDelay(0L);}builder.build();復(fù)制代碼

配置描述:

setScanMode() 設(shè)置掃描模式??蛇x擇模式主要三種:

???? ScanSettings.SCAN_MODE_LOW_POWER 低功耗模式

???? ScanSettings.SCAN_MODE_BALANCED 平衡模式

???? ScanSettings.SCAN_MODE_LOW_LATENCY 高功耗模式

????從上到下,會越來越耗電,但掃描間隔越來越短,即掃描速度會越來越快。

setCallbackType() 設(shè)置回調(diào)類型

可選擇模式主要三種:?

ScanSettings.CALLBACK_TYPE_ALL_MATCHES 數(shù)值: 1

尋找符合過濾條件的藍牙廣播,如果沒有設(shè)置過濾條件,則返回全部廣播包

ScanSettings.CALLBACK_TYPE_FIRST_MATCH 數(shù)值: 2

僅針對與篩選條件匹配的第一個廣播包觸發(fā)結(jié)果回調(diào)。

ScanSettings.CALLBACK_TYPE_MATCH_LOST 數(shù)值: 4

????回調(diào)類型一般設(shè)置ScanSettings.CALLBACK_TYPE_ALL_MATCHES,有過濾條件時過濾,返回符合過濾條件的藍牙廣播。無過濾條件時,返回全部藍牙廣播。

setMatchMode() 設(shè)置藍牙LE掃描濾波器硬件匹配的匹配模式

一般設(shè)置ScanSettings.MATCH_MODE_STICKY

Bluetoothadapter.isOffloadedScanBatchingSupported()

判斷當前手機藍牙芯片是否支持批處理掃描。如果支持掃描則使用批處理掃描,可通過ScanSettings.Builder對象調(diào)用setReportDelay(Long)方法來設(shè)置藍牙LE掃描的報告延遲的時間(以毫秒為單位)來啟動批處理掃描模式。

ScanSettings.Builder.setReportDelay(Long);

當設(shè)備藍牙芯片支持批處理掃描時,用來設(shè)置藍牙LE掃描的報告延遲的時間(以毫秒為單位)。

????該參數(shù)默認為 0,如果不修改它的值,則默認只會在onScanResult(int,ScanResult)中返回掃描到的藍牙設(shè)備,不會觸發(fā)不會觸發(fā)onBatchScanResults(List)方法。

????設(shè)置為0以立即通知結(jié)果,不開啟批處理掃描模式。即ScanCallback藍牙回調(diào)中,不會觸發(fā)onBatchScanResults(List)方法,但會觸發(fā)onScanResult(int,ScanResult)方法,返回掃描到的藍牙設(shè)備。

????當設(shè)置的時間大于0L時,則會開啟批處理掃描模式。即觸發(fā)onBatchScanResults(List)方法,返回掃描到的藍牙設(shè)備列表。但不會觸發(fā)onScanResult(int,ScanResult)方法。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容