一年多了巩步,沒有來簡書寫文章旁赊,今天恰逢風(fēng)高氣爽,總結(jié)下個人對BLE藍牙一些問題
什么是經(jīng)典藍牙椅野,什么是BLE藍牙终畅,什么是雙模藍牙
A、經(jīng)典藍牙:
1)竟闪、傳聲音:如藍牙耳機离福、藍牙音箱。藍牙設(shè)計的時候就是為了傳聲音的炼蛤,所以是近距離的音頻傳輸?shù)牟欢x擇⊙現(xiàn)在也有基于WIFI的音頻傳輸方案,例如Airplay等鲸湃,但是WIFI功耗比藍牙大很多赠涮,設(shè)備無法做到便攜。因此固定的音響有WIFI的暗挑,移動的如耳機笋除、便攜音箱清一色都是基于經(jīng)典藍牙協(xié)議的。
2)炸裆、傳大量數(shù)據(jù): 例如某些工控場景垃它,使用Android或Linux主控,外掛藍牙遙控設(shè)備的,可以使用經(jīng)典藍牙里的SPP協(xié)議国拇,當作一個無線串口使用洛史。速度比BLE傳輸快多了。
B酱吝、BLE藍牙:
1)也殖、耗電低,數(shù)據(jù)量小务热,如遙控類(鼠標忆嗜、鍵盤),傳感設(shè)備(心跳帶崎岂、血壓計捆毫、溫度傳感器、共享單車鎖冲甘、智能鎖绩卤、防丟器、室內(nèi)定位)江醇。
2)濒憋、目前手機和智能硬件通信的性價比最高的手段,直線距離約50米嫁审,一節(jié)5號電池能用一年跋炕,傳輸模組成本便宜,遠比WIFI律适、4G等大數(shù)據(jù)量的通信協(xié)議更實用。雖然藍牙距離近了點遏插,但勝在直連手機捂贿,價格超便宜。以室內(nèi)定位為例胳嘲,商場每家門店掛個藍牙beacon厂僧,就可以對手機做到精度10米級的室內(nèi)定位,將來的藍牙5.1更可以實現(xiàn)厘米級室內(nèi)定位了牛。
C颜屠、雙模藍牙:
1)、智能電視遙控器:很多智能電視配的遙控器帶有語音識別鹰祸,需要用經(jīng)典藍牙才能傳輸聲音甫窟。而如果做復(fù)雜的按鍵,例如原本鍵盤表上沒有的功能蛙婴,經(jīng)典藍牙的HID按鍵協(xié)議就不行了粗井,得用BLE做私有協(xié)議。
2)、降噪耳機:很多降噪耳機上通過APP來調(diào)節(jié)降噪效果浇衬,也是通過BLE來實現(xiàn)的私有通信協(xié)議懒构。
開發(fā)過程中遇到的問題
1、已配對的列表消失問題
-
BluetoothAdapter.getDefaultAdapter().getBondedDevices();
此方法是獲取設(shè)備已經(jīng)pair過的列表耘擂,有時候沒有去removeBond
胆剧,明明有一個設(shè)備,但是某種情況下消失醉冤,也就是getBondedDevices().size=0
目前傾向于是系統(tǒng)做了一些操作
2赞赖、在掃描BLE設(shè)備的時候,有幾率會發(fā)現(xiàn)由于BLE地掃描導(dǎo)致地整個App頁面卡
3冤灾、進行BLE的基本操作導(dǎo)致BLE Crash問題
4前域、關(guān)于不斷連接BLE的時候,底層報錯status =133的問題
- 復(fù)現(xiàn)方法:不斷地disconnect 和 connect
- 補充說明:連接BLE韵吨,假如BLE已經(jīng)connect到另外的設(shè)備匿垄,這個時候status 133,是正彻榉郏現(xiàn)象椿疗,但是我們的平板發(fā)生的概率很大
5、在藍牙已經(jīng)打開的狀態(tài)下糠悼,開啟藍牙掃描的時候届榄,getBluetoothLeScanner()==null
,然后設(shè)備不能開啟掃描
6倔喂、藍牙的建立攔截的過程中铝条,發(fā)生無響應(yīng)的狀態(tài)
- 藍牙連接過程的說明:開始BLE掃描-->獲取到目標設(shè)備-->開始pair-->成功--->開始連接-->連接成功--->changeMTU-->onMtuChanged() 這個是GattCallback的內(nèi)置方法,回調(diào)--->接著開始 gat.discoverServices();--->秘鑰協(xié)商--->真正的連接狀態(tài)
- 發(fā)現(xiàn)問題:當手動調(diào)用 changeMTU 后席噩,onMtuChanged方法不回調(diào)班缰,就卡在中間
開發(fā)中優(yōu)化的建議
-
1、藍牙掃描設(shè)備
我實現(xiàn)的方式是通過開啟一個線程池悼枢,這個是Executors.newSingleThreadExecutor()
,
/**
* How to understand:
* 1. A new task will be placed in corePool, the core thread pool, the size is 1, where corePoolSize=maximumPoolSize=1
* 2. When the core thread pool is full, it will be placed on the LinkedBlockingQueue. If the LinkedBlockingQueue is a bounded queue, a bounded blocking queue composed of a linked list structure, and it is also full. size=Integer.MAX_VALUE
* 3. Create a new task in the thread pool smaller than the maximum until the maximum thread pool is full
* 4. It will go to the rejection queue to see what kind of rejection plan it is, and then just like that
* 5. Because defaultHandler = new AbortPolicy(); it is possible to throw an exception, although this chance is small
* 6. The execution time of the execute method is usually a slow process. If it is a fast process, you will not be placed in the thread pool, so I think this is suitable for download initialization
*/
/**
* 如何理解:
* 1.會在corePool埠忘,核心線程池中放置一個新任務(wù),大小為1馒索,其中corePoolSize=maximumPoolSize=1
* 2.當核心線程池滿時莹妒,會放到LinkedBlockingQueue上。 如果LinkedBlockingQueue是一個有界隊列绰上,一個由鏈表結(jié)構(gòu)組成的有界阻塞隊列旨怠,它也是滿的。 大小=整數(shù).MAX_VALUE
* 3.在線程池中創(chuàng)建一個小于最大值的新任務(wù)渔期,直到最大值線程池滿
* 4.它會去拒絕隊列看它是什么拒絕計劃运吓,然后就這樣
* 5. 因為 defaultHandler = new AbortPolicy(); 有可能拋出異常渴邦,雖然這個機會很小
* 6.execute方法的執(zhí)行時間通常是一個緩慢的過程。 如果是快進程拘哨,你就不會被放到線程池中谋梭,所以我覺得這個適合下載初始化
*/
2、在實際過程中倦青,BLE發(fā)生過假連接的情況瓮床,所以判斷BLE是否是在連接的狀態(tài)很關(guān)鍵
主要是通過 反射去獲取連接狀態(tài)Method method = bluetoothDeviceClass.getDeclaredMethod("isConnected", (Class[]) null);
3、 BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner()
在BLE關(guān)閉的狀態(tài)下产镐,這個會null隘庄,但是我在開發(fā)過程中其實也發(fā)現(xiàn)了,也會為null癣亚,所以需要判斷null丑掺,在掃描設(shè)備的時候也要主要判斷,要不然述雾,整個子線程crash了街州,都沒有反應(yīng)4、關(guān)于一直掃描Pair的問題玻孟,由于安卓系統(tǒng)各不相同唆缴,所有在某些板子上有可能發(fā)生一直Pair的情況,
個人建議如果Pair失敗的次數(shù)過多的情況下黍翎,可以偷偷的打開和關(guān)閉BLE即可解決問題5面徽、藍牙的建立攔截的過程中,發(fā)生無響應(yīng)的狀態(tài)
可以偷偷的打開和關(guān)閉BLE即可解決問題匣掸,具體原因我大約知道趟紊,但是不敢肯定,所以不貼原因了6旺聚、關(guān)于判斷是否BLE連接上了织阳,我看過好多開源的工程都在這個方法中判斷onConnectionStateChange
但是其實不太好,說下我的理由砰粹,在這里判斷連接失敗是可以的,但是呢造挽?連接成功呢碱璃,如果項目不涉及到秘鑰協(xié)商的東西,那應(yīng)該可以饭入,但是 嵌器,我從事過的商業(yè)項目都是加密的點對點的加密,所以當一個完善的項目應(yīng)該是秘鑰協(xié)商之后流程 onConnectionStateChange--> changeMTU(int) --->onMtuChanged()--> gatt.discoverServices()-->onCharacteristicRead()--->根據(jù)最后讀取的特征值去判斷谐丢,因為商業(yè)項目有可能會讀取多個硬件信息爽航,版本號蚓让,等等,連接成功讥珍,可以正常通訊
7历极、gatt 不用了需要close,而不是disconnet
8衷佃、如果你正在實現(xiàn)一個BLE模塊趟卸,建議使用狀態(tài)模式去實現(xiàn)代碼,但是也特備要注意下狀態(tài)管理不能夠亂氏义,如果亂了锄列,狀態(tài)模式反而比較麻煩
9、重連通過 connectByName Or connectByAddress 這種方法去獲取到的BluetoothDevice其實地址是同一個惯悠,所以直接使用BluetoothDevice去connect即可邻邮,不要花花腸子那么多
10、一般情況下克婶,BLE設(shè)備有恢復(fù)出廠設(shè)置筒严,假如這個時候使用虛擬mac地址的,代碼需要更多的判斷鸠补,所以項目維持一個目前連接的設(shè)備萝风,當然這種的沒有考慮到mesh,如果mesh那會更加的復(fù)雜
11紫岩、BLE自動重連规惰,在實際過程中,如果BLE斷電然后上電泉蝌,是需要重新連接的歇万,但是建議做個延遲,因為我和硬件工程師聯(lián)調(diào)的時候勋陪,發(fā)現(xiàn)App重連的指令太快贪磺,導(dǎo)致的秘鑰協(xié)商不通過的問題
12、BLE回調(diào)的所有的消息其實都在一個線程池中诅愚,所以如果特別的場景寒锚,需要考慮到多線程同步的問題。违孝。刹前。
13、status =133 是個正常的現(xiàn)象雌桑,如果概率很大喇喉,需要做處理
14、2021年9月15日晚上:
我發(fā)現(xiàn)一個現(xiàn)象:
BLE 移除已經(jīng) 配對列表過程 :removeBond -- ---->收到系統(tǒng)廣播 BOND_NONE (從開始到這一步 大約有4s)
如果在removeBond 和 收到系統(tǒng)廣播中間校坑,以很快的速度去 pair 設(shè)備拣技,那么必定發(fā)生pair不成功千诬,只有重啟才能解決
新的問題,有點意思膏斤,不知道是不是系統(tǒng)的問題Or其他的問題
'2022.5.9 android藍牙多次后,android – 如何防止BluetoothGattCallback一次多次執(zhí)行
https://stackoverflow.com/questions/33274009/how-to-prevent-bluetoothgattcallback-from-being-executed-multiple-times-at-a-tim---- Updated Answer ----
I have found that its better to call bluetoothGatt.close() inside the onConnectionStateChanged callback. When you issue a disconnect it sends a message to the bluetooth device to request disconnect. Then once it responds you get the callback and close the bluetooth gatt connection. By waiting for the callback and not opening another gatt connection until its fully closed it seems to prevent multiple gatt objects from getting connected to the app.
其實就是需要在disconnect后徐绑,首先調(diào)用 GATT.colse()
- 2022.6.10 ,onMtuChanged status的狀態(tài)異常的問題
- 1.手機我測試到的問題是 onMtuChanged mtu 23 status 10 掸绞,由于 BluetoothGatt.GATT_SUCCESS ==0
- 2.所以走不到 gatt.discoverServices(); 那么連接的狀態(tài)也會中斷泵三,這個也應(yīng)該是平板遇到的問題
- 這里要怎么處理呢?
- 建議要處理異常衔掸?不能僅僅只處理成功的狀態(tài)
- 為什么會導(dǎo)致這個現(xiàn)象 烫幕,以下是我的猜測
- 是我在頻繁的使用手機 connect
- 2.本來BLE 是connect的狀態(tài),還在繼續(xù)connect 敞映,無視 BLE的 狀態(tài)
- 建議要處理異常衔掸?不能僅僅只處理成功的狀態(tài)
/**
*
* 1.手機我測試到的問題是 onMtuChanged mtu 23 status 10 较曼,由于 BluetoothGatt.GATT_SUCCESS ==0
* 2.所以走不到 gatt.discoverServices(); 那么連接的狀態(tài)也會中斷,這個也應(yīng)該是平板遇到的問題
*
* 這里要怎么處理呢振愿?
* 要處理異常捷犹?不能僅僅只處理成功的狀態(tài)
* 為什么會導(dǎo)致這個現(xiàn)象 ,以下是我的猜測
* 1. 是我在頻繁的使用手機 connect
* 2.本來BLE 是connect的狀態(tài)冕末,還在繼續(xù)connect 萍歉,無視 BLE的 狀態(tài)
* @param gatt
* @param mtu
* @param status
*/
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
LogUtils.Sming("onMtuChanged mtu " + mtu + " status " + status);
BluetoothMachine.this.mtu = mtu;
// TODO: 2022/4/9 監(jiān)控手機是否 也存在 mtu 不回調(diào)的問題
if (status == BluetoothGatt.GATT_SUCCESS) {
gatt.discoverServices();
ConnectionStateChangeManager.getInstance().notifyDeviceConnectStateObserver(ConnectionState.DISCOVER_SERVICES);
LogUtils.Sming(" onMtuChanged gatt.discoverServices() ");
}
}
- 如果斷開,藍牙設(shè)備档桃,如果在同步的去調(diào)用 Method method = bluetoothDeviceClass.getDeclaredMethod("isConnected", (Class[]) null); 枪孩,會發(fā)現(xiàn)是連接狀態(tài),但是其實Gatt 已經(jīng)關(guān)閉了藻肄,為什么 蔑舞? 2022.5.9號
Tip:
-
一個函數(shù)只做一件事情,它改做的事
- 一個函數(shù)嘹屯,如果可能攻询,讓它保持在20行以內(nèi)(優(yōu)秀), 或者保持一個屏的高度以內(nèi)(不錯)
- 過于深度的if else邏輯問題, 要考慮抽取州弟、分流的方式钧栖,并拆開層級
- 代碼塊不能粘在一起,關(guān)鍵位置隔行婆翔,以及組成代碼塊 或者 新的函數(shù)