詳解Android 藍(lán)牙通信方式總結(jié)
1.摘要
Android手機(jī)間通過藍(lán)牙方式進(jìn)行通信,有兩種常見的方式,一種是socket方式,另一種是通過Gatt Server(Android 5.0以后)通信,socket方式最為簡單,但是很多低功耗的藍(lán)牙設(shè)備,如單片機(jī)上的藍(lán)牙模塊可能不支持;而Gatt方式相對(duì)比較復(fù)雜。其實(shí)無論是socket方式還是Gatt,Android設(shè)備間藍(lán)牙通信都是一種C/S(client-server)模式。
本文基于兩種通信方式,進(jìn)行詳細(xì)展開,并推薦了開源項(xiàng)目,建議配合學(xué)習(xí)。
關(guān)鍵詞
(1)Bluetooth
藍(lán)牙(Bluetooth):藍(lán)牙,是一種支持設(shè)備短距離通信(一般10m內(nèi))的無線電技術(shù),能在包括移動(dòng)電話、PDA、無線耳機(jī)、筆記本電腦、相關(guān)外設(shè)等眾多設(shè)備之間進(jìn)行無線信息交換。利用“藍(lán)牙”技術(shù),能夠有效地簡化移動(dòng)通信終端設(shè)備之間的通信,也能夠成功地簡化設(shè)備與因特網(wǎng)Internet之間的通信,從而使數(shù)據(jù)傳輸變得更加迅速高效,為無線通信拓寬道路。
(2) UUID
UUID(Universally Unique Identifier):用于標(biāo)識(shí)藍(lán)牙服務(wù)以及通訊特征訪問屬性,不同的藍(lán)牙服務(wù)和屬性使用不同的訪問方法。
(3)服務(wù)UUID
服務(wù)UUID(Service UUID):不同的服務(wù)(Service)應(yīng)該有不同的編號(hào)(UUID),用以區(qū)分不同的服務(wù)(Service)。
(4)特征值UUID
特征值UUID(Characteristic UUID):特性(Characteristic) 是依附于某個(gè)服務(wù)(Service)的
(5)屬性(Property) (5.1)Read: 讀屬性
Read: 讀屬性,具有這個(gè)屬性的特性是可讀的,也就是說這個(gè)屬性允許手機(jī)來讀取一些信息。手機(jī)可以發(fā)送指令來讀取某個(gè)具有讀屬性UUID的信息。
(5.2)Notify: 通知屬性
Notify: 通知屬性, 具有這個(gè)屬性的特性是可以發(fā)送通知的,也就是說具有這個(gè)屬性的特性(Characteristic)可以主動(dòng)發(fā)送信息給手機(jī)。
(5.3)Write: 寫屬性
Write: 寫屬性, 具有這個(gè)屬性的特性是可以接收寫入數(shù)據(jù)的。通常手機(jī)發(fā)送數(shù)據(jù)給藍(lán)模塊就是通過這個(gè)屬性完成的。這個(gè)屬性在Write 完成后,會(huì)發(fā)送寫入完成結(jié)果的反饋給手機(jī),然后手機(jī)再可以寫入下一包或處理后續(xù)業(yè)務(wù),這個(gè)屬性在寫入一包數(shù)據(jù)后,需要等待應(yīng)用層返回寫入結(jié)果,速度比較慢。
(5.4)WriteWithout Response:寫屬性
WriteWithout Response:寫屬性,從字面意思上看,只是寫,不需要返回寫的結(jié)果,這個(gè)屬性的特點(diǎn)是不需要應(yīng)用層返回,完全依靠協(xié)議層完成,速度快,但是寫入速度超過協(xié)議處理速度的時(shí)候,會(huì)丟包。
(6) GATT
GATT(Generic Attribute Profile):中文名叫通用屬性協(xié)議,它定義了services和characteristic兩種東西來完成低功耗藍(lán)牙設(shè)備之間的數(shù)據(jù)傳輸。它是建立在通用數(shù)據(jù)協(xié)議Attribute Protocol (ATT),之上的,ATT把services和characteristic以及相關(guān)的數(shù)據(jù)保存在一張簡單的查找表中,該表使用16-bit的id作為索引。
(7)profile
profile可以理解為一種規(guī)范,一個(gè)標(biāo)準(zhǔn)的通信協(xié)議,它存在于從機(jī)中。藍(lán)牙組織規(guī)定了一些標(biāo)準(zhǔn)的profile,例如 HID OVER GATT ,防丟器 ,心率計(jì)等。每個(gè)profile中會(huì)包含多個(gè)service,每個(gè)service代表從機(jī)的一種能力。
2. Bluetooth Socket
推薦開源項(xiàng)目:https://github.com/Zweo/Bluetooth (https://github.com/zolty-lionheart/Bluetooth)
以該項(xiàng)目demo為例介紹
藍(lán)牙端口監(jiān)聽接口和TCP端口類似:Socket和ServerSocket類。在服務(wù)器端,使用BluetoothServerSocket類來創(chuàng)建一個(gè) 監(jiān)聽服務(wù)端口。當(dāng)一個(gè)連接被BluetoothServerSocket所接受,它會(huì)返回一個(gè)新的BluetoothSocket來管理該連接。在客戶 端,使用一個(gè)單獨(dú)的BluetoothSocket類去初始化一個(gè)外接連接和管理該連接。
最通常使用的藍(lán)牙端口是RFCOMM,它是被Android API支持的類型。RFCOMM是一個(gè)面向連接,通過藍(lán)牙模塊進(jìn)行的數(shù)據(jù)流傳輸方式,它也被稱為串行端口規(guī)范(Serial Port Profile,SPP)。
為了創(chuàng)建一個(gè)BluetoothSocket去連接到一個(gè)已知設(shè)備,使用方法 BluetoothDevice.createRfcommSocketToServiceRecord()。然后調(diào)用connect()方法去嘗試一個(gè) 面向遠(yuǎn)程設(shè)備的連接。這個(gè)調(diào)用將被阻塞指導(dǎo)一個(gè)連接已經(jīng)建立或者該鏈接失效。
為了創(chuàng)建一個(gè)BluetoothSocket作為服務(wù)端(或者“主機(jī)”),查看BluetoothServerSocket文檔。
每當(dāng)該端口連接成功,無論它初始化為客戶端,或者被接受作為服務(wù)器端,通過getInputStream()和getOutputStream()來打開IO流,從而獲得各自的InputStream和OutputStream對(duì)象
BluetoothSocket類線程安全。特別的,close()方法總會(huì)馬上放棄外界操作并關(guān)閉服務(wù)器端口。
注意:需要BLUETOOTH權(quán)限。
2.1 Server端
private static final String UUIDString = "00001101-0000-1000-8000-00805F9B34FB"; //開啟服務(wù)器 private class ServerThread extends Thread { @Override public void run() { try { /* 創(chuàng)建一個(gè)藍(lán)牙服務(wù)器 * 參數(shù)分別:服務(wù)器名稱、UUID */ mServerSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord(PROTOCOL_SCHEME_RFCOMM, UUID.fromString(UUIDString)); while (true){ Log.d("server", "wait cilent connect..."); Message msg = new Message(); msg.obj = "請稍候,正在等待客戶端的連接..."; msg.what = WAITING_FOR_CLIENT; linkDetectedHandler.sendMessage(msg); /* 接受客戶端的連接請求 */ BluetoothSocket socket = mServerSocket.accept(); socketMap.put(socket.getRemoteDevice().getAddress(), socket); // remoteDeviceMap.put(socket.getRemoteDevice().getAddress(),socket.getRemoteDevice()); Log.d("server", "accept success !"); Message msg2 = new Message(); String info = "客戶端已經(jīng)連接上!可以發(fā)送信息。"; msg2.obj = info; msg.what = CONNECTED_CLIENT; linkDetectedHandler.sendMessage(msg2); //啟動(dòng)接受數(shù)據(jù) ReadThread mreadThread = new ReadThread(socket.getRemoteDevice().getAddress()); readThreadMap.put(socket.getRemoteDevice().getAddress(),mreadThread); mreadThread.start(); } } catch (IOException e) { e.printStackTrace(); } } }
2.2 Server
//開啟客戶端 private class ClientThread extends Thread { private String remoteAddress; public ClientThread(String remoteAddress) { this.remoteAddress = remoteAddress; } @Override public void run() { try { //創(chuàng)建一個(gè)Socket連接:只需要服務(wù)器在注冊時(shí)的UUID號(hào) BluetoothDevice device = bluetoothAdapter.getRemoteDevice(remoteAddress); BluetoothSocket socket = device.createRfcommSocketToServiceRecord(UUID.fromString(UUIDString)); //連接 Message msg2 = new Message(); msg2.obj = "請稍候,正在連接服務(wù)器:" + remoteAddress; msg2.what = IS_CONNECTING_SERVER; linkDetectedHandler.sendMessage(msg2); socket.connect(); socketMap.put(remoteAddress, socket); Message msg = new Message(); msg.obj = remoteAddress; msg.what = CONNECTED_SERVER; linkDetectedHandler.sendMessage(msg); //啟動(dòng)接受數(shù)據(jù) ReadThread mreadThread = new ReadThread(remoteAddress); readThreadMap.put(remoteAddress,mreadThread); mreadThread.start(); } catch (IOException e) { e.printStackTrace(); socketMap.remove(remoteAddress); Log.e("connect", e.getMessage(), e); Message msg = new Message(); msg.obj = "連接服務(wù)端異常!斷開連接重新試一試。"+e.getMessage(); msg.what = CONNECT_SERVER_ERROR; linkDetectedHandler.sendMessage(msg); } } }
3. Bluetooth GATT
推薦開源項(xiàng)目:https://github.com/dingpwen/bl_communication (https://github.com/zolty-lionheart/bl_communication)
以該項(xiàng)目demo為例介紹
3.1 Server
private fun setupServer() { val gattService = BluetoothGattService(Constants.BLE_SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY) val characteristicRead = BluetoothGattCharacteristic(Constants.BLE_READ_UUID, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ) val descriptor = BluetoothGattDescriptor(Constants.BLE_DESC_UUID, BluetoothGattCharacteristic.PERMISSION_WRITE) characteristicRead.addDescriptor(descriptor) gattService.addCharacteristic(characteristicRead) val characteristicWrite = BluetoothGattCharacteristic(Constants.BLE_WRITE_UUID, BluetoothGattCharacteristic.PROPERTY_WRITE or BluetoothGattCharacteristic.PROPERTY_READ or BluetoothGattCharacteristic.PROPERTY_NOTIFY, BluetoothGattCharacteristic.PERMISSION_WRITE) gattService.addCharacteristic(characteristicWrite) Log.d("wenpd", "startGattServer:stagattServicetus=$gattService") mGattServer.addService(gattService) }
3.2 Client
private class GattClientCallback extends BluetoothGattCallback { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); if (status == BluetoothGatt.GATT_FAILURE) { disconnectGattServer(); return; } else if (status != BluetoothGatt.GATT_SUCCESS) { disconnectGattServer(); return; } if (newState == BluetoothProfile.STATE_CONNECTED) { mConnected = true; gatt.discoverServices(); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { disconnectGattServer(); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); Log.d(TAG, "onServicesDiscovered status:" + status); if (status != BluetoothGatt.GATT_SUCCESS) { return; } BluetoothGattService service = gatt.getService(Constants.SERVICE_UUID); BluetoothGattCharacteristic characteristic = service.getCharacteristic(Constants.CHARACTERISTIC_UUID); characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT); mInitialized = gatt.setCharacteristicNotification(characteristic, true); } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); byte[] messageBytes = characteristic.getValue(); /*for(int i = 0, j = messageBytes.length -1; i < j; ++i, --j) { byte temp = messageBytes[i]; messageBytes[i] = messageBytes[j]; messageBytes[j] = temp; }*/ String messageString = new String(messageBytes, StandardCharsets.UTF_8); Log.d(TAG,"Received message: " + messageString); setReceivedData(messageString); } }
參考文獻(xiàn)
1.Android Phone藍(lán)牙通信方式總結(jié)(Socket與Gatt)
2.Bluetooth之BluetoothSocket
3.全面且簡單明了的藍(lán)牙服務(wù)及UUID介紹
4.Android BLE藍(lán)牙開發(fā)-讀寫數(shù)據(jù) 獲取UUID
到此這篇關(guān)于詳解Android 藍(lán)牙通信方式總結(jié)的文章就介紹到這了,更多相關(guān)Android 藍(lán)牙通信內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Android藍(lán)牙開發(fā)深入解析
- 詳解Android——藍(lán)牙技術(shù) 帶你實(shí)現(xiàn)終端間數(shù)據(jù)傳輸
- Android單片機(jī)與藍(lán)牙模塊通信實(shí)例代碼
- Android Bluetooth藍(lán)牙技術(shù)使用流程詳解
- 分享Android 藍(lán)牙4.0(ble)開發(fā)的解決方案
- Android 獲取藍(lán)牙Mac地址的正確方法
- Android手機(jī)通過藍(lán)牙連接佳博打印機(jī)的實(shí)例代碼
- android實(shí)現(xiàn)藍(lán)牙文件發(fā)送的實(shí)例代碼,支持多種機(jī)型
- Android實(shí)現(xiàn)藍(lán)牙(BlueTooth)設(shè)備檢測連接
- Android學(xué)習(xí)筆記之藍(lán)牙功能
相關(guān)文章
安卓(Android)動(dòng)態(tài)創(chuàng)建多個(gè)按鈕并添加監(jiān)聽事件
本文主要介紹Android動(dòng)態(tài)創(chuàng)建多個(gè)按鈕并給每個(gè)按鍵添加監(jiān)聽事件,在做Android項(xiàng)目會(huì)經(jīng)常遇到的,希望對(duì)需要用到的同學(xué)有所幫助2016-07-07Android Studio真機(jī)無線連接USB設(shè)備調(diào)試運(yùn)行詳解流程
你在Android Studio寫app時(shí)是否也有想過如果可以不用數(shù)據(jù)線連接手機(jī)調(diào)試運(yùn)行就好了?如果需要取出數(shù)據(jù)線插接的話我肯定是嫌麻煩的,但是模擬器有時(shí)候需要測試一些需要硬件支持的功能時(shí)又不管用,所以最好的測試還是在真機(jī)上,本篇教你扔掉數(shù)據(jù)線來無線調(diào)試2021-11-11Android實(shí)現(xiàn)QQ手機(jī)管家懸浮小火箭效果
這篇文章主要介紹了Android實(shí)現(xiàn)QQ手機(jī)管家懸浮小火箭效果,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05Android 再按一次返回鍵退出程序?qū)崿F(xiàn)思路
用戶退出應(yīng)用前給出一個(gè)提示是很有必要的,因?yàn)榭赡苁怯脩舨⒉徽娴南胪顺?,而只是一不小心按下了返回鍵,大部分應(yīng)用的做法是在應(yīng)用退出去前給出一個(gè)Dialog提示框;個(gè)人覺得再按一次返回鍵退出程序很有必要,接下來介紹一些簡單實(shí)現(xiàn)2013-01-01Android intent之間復(fù)雜參數(shù)傳遞方法詳解
這篇文章主要介紹了Android intent之間復(fù)雜參數(shù)傳遞方法,較為詳細(xì)的分析了Android中intent參數(shù)傳遞的常見方法與使用技巧,需要的朋友可以參考下2016-10-10Android?配合Mat工具監(jiān)聽查找內(nèi)存泄漏的操作方法
這篇文章主要介紹了Android?配合Mat工具監(jiān)聽查找內(nèi)存泄漏問題,使用Android Studio Profiler查看內(nèi)存的操作,本文通過圖文實(shí)例相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05Android引用開源框架通過AsyncHttpClient實(shí)現(xiàn)文件上傳
這篇文章主要介紹了Android引用開源框架通過AsyncHttpClient實(shí)現(xiàn)文件上傳,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01Android 基于agora 開發(fā)視頻會(huì)議的代碼
這篇文章主要介紹了Android 基于agora 開發(fā)視頻會(huì)議,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01