Android 藍牙的一些知識

低功耗藍牙

Android BLE API 簡介

BluetoothAdapter

BluetoothAdapter 擁有基本的藍牙操作闯睹,例如開啟藍牙掃描柿究,使用已知的 MAC 地址 (BluetoothAdapter#getRemoteDevice)實例化一個 BluetoothDevice 用于連接藍牙設(shè)備的操作等等僚纷。

BluetoothDevice

代表一個遠程藍牙設(shè)備梢睛。這個類可以讓你連接所代表的藍牙設(shè)備或者獲取一些有關(guān)它的信息逛裤,例如它的名字誊抛,地址和綁定狀態(tài)等等狈蚤。

BluetoothGatt

這個類提供了 Bluetooth GATT 的基本功能困肩。例如重新連接藍牙設(shè)備,發(fā)現(xiàn)藍牙設(shè)備的 Service 等等脆侮。

BluetoothGattService

這一個類通過 BluetoothGatt#getService 獲得锌畸,如果當前服務(wù)不可見那么將返回一個 null。這一個類對應(yīng)上面說過的 Service靖避。我們可以通過這個類的 getCharacteristic(UUID uuid) 進一步獲取 Characteristic 實現(xiàn)藍牙數(shù)據(jù)的雙向傳輸潭枣。

BluetoothGattCharacteristic

這個類對應(yīng)上面提到的 Characteristic比默。通過這個類定義需要往外圍設(shè)備寫入的數(shù)據(jù)和讀取外圍設(shè)備發(fā)送過來的數(shù)據(jù)。

  • 掃描藍牙

在 BluetoothAdapter 中盆犁,我們可以看到有兩個掃描藍牙的方法命咐。第一個方法可以指定只掃描含有特定 UUID Service 的藍牙設(shè)備,第二個方法則是掃描全部藍牙設(shè)備谐岁。

boolean startLeScan(UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback)

boolean startLeScan(BluetoothAdapter.LeScanCallback callback)
@Override public void onLeScan(BluetoothDevice bluetoothDevice, int rssi, byte[] scanRecord) { }
 bluetoothDevice:藍牙設(shè)備的類醋奠,可以通過這個類建立藍牙連接獲取關(guān)于這一個設(shè)備的一系列詳細的參數(shù),例如名字伊佃,MAC 地址等等窜司。 rssi:藍牙的信號強弱指標。 scanRecord:藍牙廣播出來的數(shù)據(jù)锭魔。

//停止掃描 void stopLeScan(BluetoothAdapter.LeScanCallback callback)

5.0 系統(tǒng)后添加

public void startScan(final ScanCallback callback)

mBLEScanner = mBluetoothAdapter.getBluetoothLeScanner(); mBLEScanner.startScan(mScanCallback); //停止掃描 mBLEScanner.stopScan(mScanCallback); 
private ScanCallback mScanCallback = new ScanCallback() { 
@Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); //當發(fā)現(xiàn)一個外設(shè)時回調(diào)此方法例证。 } @Override public void onBatchScanResults(List<ScanResult> results) { super.onBatchScanResults(results); //在此返回一個包含所有掃描結(jié)果的列表集,包括以往掃描到的結(jié)果迷捧。 } @Override public void onScanFailed(int errorCode) { super.onScanFailed(errorCode); //掃描失敗后的處理。 } };
  • 連接
public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback) contex:You know autoConnect:表示是否需要自動連接胀葱。如果設(shè)置為 true, 表示如果設(shè)備斷開了漠秋,會不斷的嘗試自動連接。設(shè)置為 false 表示只進行一次連接嘗試抵屿。 callback:連接后進行的一系列操作的回調(diào)庆锦。

void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) 
status:嵌入式可以自定義的比如:19 藍牙門鎖主動斷開。 newState:2(STATE_CONNECTED)連接成功轧葛。0(STATE_DISCONNECTED)連接失敗搂抒。 只能是 2 或者0 
//-------源碼部分---------- 
/**
             * Client connection state changed
             * @hide
             */
            @Override
            public void onClientConnectionState(int status, int clientIf,
                                                boolean connected, String address) {
                if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
                                 + " clientIf=" + clientIf + " device=" + address);
                if (!address.equals(mDevice.getAddress())) {
                    return;
                }
                int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
                                               BluetoothProfile.STATE_DISCONNECTED;

                runOrQueueCallback(new Runnable() {
                    @Override
                    public void run() {
                        if (mCallback != null) {
                            mCallback.onConnectionStateChange(BluetoothGatt.this, status,
                                                              profileState);
                        }
                    }
                });

                synchronized(mStateLock) {
                    if (connected) {
                        mConnState = CONN_STATE_CONNECTED;
                    } else {
                        mConnState = CONN_STATE_IDLE;
                    }
                }

                synchronized(mDeviceBusy) {
                    mDeviceBusy = false;
                }
            }

  • 發(fā)現(xiàn)服務(wù)
gatt.discoverServices() 連接成功后調(diào)用。
@Override public void onServicesDiscovered(BluetoothGatt gatt, int status) {} 
在藍牙設(shè)備中, 其包含有多個BluetoothGattService, 而每個BluetoothGattService中又包含有多個BluetoothGattCharacteristic尿扯。
 當onServicesDiscovered()回調(diào)的 status == BluetoothGatt.GATT_SUCCESS求晶, 可以進行獲取service,根據(jù)嵌入式定義的 讀寫UUID 獲取到readCharacteristic。 
gatt.setCharacteristicNotification(readCharacteristic, true); 不設(shè)置不會接受到數(shù)據(jù)衷笋。 此處可獲取到writeCharacteristic 用于發(fā)送數(shù)據(jù)的操作芳杏。 
-------其他方式獲取方法-------- BluetoothGattService getService(UUID uuid) BluetoothGattCharacteristic getCharacteristic(UUID uuid)
  • 藍牙收到數(shù)據(jù)

app 獲取到藍牙數(shù)據(jù)的出口

@Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic ch) {} 
mReceivedData = ch.getValue(); 此處可以根據(jù)gatt 的uuid 來區(qū)分不同特征值的數(shù)據(jù)。
  • 發(fā)送數(shù)據(jù)
boolean ret = writeCharacteristic.setValue(tempData); 
gatt.writeCharacteristic(writeCharacteristic); 分包20個字節(jié)發(fā)送
  • MTU

mtu20的來源:GATT是基于ATT Protocol的辟宗,而它的 core spec里面定義了ATT的默認MTU為23個bytes爵赵,除去ATT的opcode一個字節(jié)以及ATT的handle2個字節(jié)之后,剩下的20個字節(jié)便是留給GATT的了泊脐。

public boolean requestMtu (int mtu) Added in API level 21 Request an MTU size used for a given connection. When performing a write request operation (write without response), the data sent is truncated to the MTU size. This function may be used to request a larger MTU size to be able to send more data at once. A onMtuChanged(BluetoothGatt, int, int) callback will indicate whether this operation was successful. Requires BLUETOOTH permission. Returns true, if the new MTU value has been requested successfully @Override public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { super.onMtuChanged(gatt, mtu, status); if (status == BluetoothGatt.GATT_SUCCESS) { } }

由于十分不穩(wěn)定和兼容問題空幻,很少使用。

  • AndroidQ BLE 新增COC容客, 可以用于傳輸大數(shù)據(jù)秕铛,比如用于ota升級约郁。

Bluetooth LE Connection Oriented Channels (CoC) Android 10 enables your app to use BLE CoC connections to transfer larger data streams between two BLE devices. This interface abstracts Bluetooth and connectivity mechanics to simplify implementation.

  • 斷開連接
gatt.disconnect(); gatt.close(); gatt=null;

Read More

https://www.bluetooth.com/

https://developer.android.com/guide/topics/connectivity/bluetooth-

le.html

https://developer.android.google.cn/reference/android/bluetooth/le/

其他摘要:

Dealing with errors

Now that we dealt with successful operations we need to look at the errors. There are a bunch of cases that are actually very normal that will present themselves as ‘errors’:

  • The device disconnected itself on purpose. For example, because all data has been transferred and there is nothing else to to. You will receive status 19 (GATT_CONN_TERMINATE_PEER_USER).
  • The connection timed out and the device disconnected itself. In this case you’ll get a status 8 (GATT_CONN_TIMEOUT)
  • There was an low-level error in the communication which led to the loss of the connection. Typically you would receive a status 133 (GATT_ERROR) or a more specific error code if you are lucky!
  • The stack never managed to connect in the first place. In this case you will also receive a status 133 (GATT_ERROR)
  • The connection was lost during service discovery or bonding. In this case you will want to investigate why this happened and perhaps retry the connection.

The first two cases are totally normal and there is nothing else to do than call close() and perhaps do some internal cleanup like disposing of the BluetoothGatt object.

In the other cases, you may want to do something like informing other parts of your app or showing something in the UI. If there was a communication error you might be doing something wrong yourself. Alternatively, the device might be doing something wrong. Either way, something to deal with! It is a bit up to you to what extend you want to deal with all possible cases.

Have a look at my version in my Blessed library how I did it.

Status 133 when connecting

It is very common to see a status 133 when trying to connect to a device, especially while you are developing your code. The status 133 can have many causes and some of them you can control:

  • Make sure you always call close() when there is a disconnection. If you don’t do this you’ll get a 133 for sure next time you try.
  • Make sure you always use TRANSPORT_LE when calling connectGatt()
  • Restart your phone if you see this while developing. You may have corrupted the stack by debugging and it is in a state where it doesn’t behave normal again. Restarting your phone may fix things.
  • Make sure your device is advertising. The connectGatt with autoconnect set to false times out after 30 seconds and you will receive a 133.
  • Change the batteries of your device. Devices typically start behaving erratically when they battery level is very low.

If you have tried all of the above and still see status 133 you need to simply retry the connection! This is one of the Android bugs I never managed to understand or find a workaround for. For some reason, you sometimes get a 133 when connecting to a device but if you call close() and retry it works without a problem! I suspect there is an issue with the Android cache that causes all this and the close() call puts it back in a proper state. But I am really just guessing here…If anybody figures out how to solve this one, let me know!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市如捅,隨后出現(xiàn)的幾起案子棍现,更是在濱河造成了極大的恐慌,老刑警劉巖镜遣,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件己肮,死亡現(xiàn)場離奇詭異,居然都是意外死亡悲关,警方通過查閱死者的電腦和手機谎僻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來寓辱,“玉大人艘绍,你說我怎么就攤上這事★ぃ” “怎么了诱鞠?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長这敬。 經(jīng)常有香客問我航夺,道長,這世上最難降的妖魔是什么崔涂? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任阳掐,我火速辦了婚禮,結(jié)果婚禮上冷蚂,老公的妹妹穿的比我還像新娘缭保。我一直安慰自己,他們只是感情好蝙茶,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布艺骂。 她就那樣靜靜地躺著,像睡著了一般尸闸。 火紅的嫁衣襯著肌膚如雪彻亲。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天吮廉,我揣著相機與錄音苞尝,去河邊找鬼。 笑死宦芦,一個胖子當著我的面吹牛宙址,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播调卑,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼抡砂,長吁一口氣:“原來是場噩夢啊……” “哼大咱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起注益,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤碴巾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后丑搔,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體厦瓢,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年啤月,在試婚紗的時候發(fā)現(xiàn)自己被綠了煮仇。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡谎仲,死狀恐怖浙垫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情郑诺,我是刑警寧澤夹姥,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站辙诞,受9級特大地震影響佃声,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜倘要,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望十拣。 院中可真熱鬧封拧,春花似錦、人聲如沸夭问。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缰趋。三九已至捧杉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間秘血,已是汗流浹背味抖。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留灰粮,地道東北人仔涩。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像粘舟,于是被迫代替她去往敵國和親熔脂。 傳聞我的和親對象是個殘疾皇子佩研,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容