詳解Android 藍牙通信方式總結
1.摘要
Android手機間通過藍牙方式進行通信,有兩種常見的方式,一種是socket方式,另一種是通過Gatt Server(Android 5.0以后)通信,socket方式最為簡單,但是很多低功耗的藍牙設備,如單片機上的藍牙模塊可能不支持;而Gatt方式相對比較復雜。其實無論是socket方式還是Gatt,Android設備間藍牙通信都是一種C/S(client-server)模式。
本文基于兩種通信方式,進行詳細展開,并推薦了開源項目,建議配合學習。
關鍵詞
(1)Bluetooth
藍牙(Bluetooth):藍牙,是一種支持設備短距離通信(一般10m內)的無線電技術,能在包括移動電話、PDA、無線耳機、筆記本電腦、相關外設等眾多設備之間進行無線信息交換。利用“藍牙”技術,能夠有效地簡化移動通信終端設備之間的通信,也能夠成功地簡化設備與因特網(wǎng)Internet之間的通信,從而使數(shù)據(jù)傳輸變得更加迅速高效,為無線通信拓寬道路。
(2) UUID
UUID(Universally Unique Identifier):用于標識藍牙服務以及通訊特征訪問屬性,不同的藍牙服務和屬性使用不同的訪問方法。
(3)服務UUID
服務UUID(Service UUID):不同的服務(Service)應該有不同的編號(UUID),用以區(qū)分不同的服務(Service)。
(4)特征值UUID
特征值UUID(Characteristic UUID):特性(Characteristic) 是依附于某個服務(Service)的
(5)屬性(Property) (5.1)Read: 讀屬性
Read: 讀屬性,具有這個屬性的特性是可讀的,也就是說這個屬性允許手機來讀取一些信息。手機可以發(fā)送指令來讀取某個具有讀屬性UUID的信息。
(5.2)Notify: 通知屬性
Notify: 通知屬性, 具有這個屬性的特性是可以發(fā)送通知的,也就是說具有這個屬性的特性(Characteristic)可以主動發(fā)送信息給手機。
(5.3)Write: 寫屬性
Write: 寫屬性, 具有這個屬性的特性是可以接收寫入數(shù)據(jù)的。通常手機發(fā)送數(shù)據(jù)給藍模塊就是通過這個屬性完成的。這個屬性在Write 完成后,會發(fā)送寫入完成結果的反饋給手機,然后手機再可以寫入下一包或處理后續(xù)業(yè)務,這個屬性在寫入一包數(shù)據(jù)后,需要等待應用層返回寫入結果,速度比較慢。
(5.4)WriteWithout Response:寫屬性
WriteWithout Response:寫屬性,從字面意思上看,只是寫,不需要返回寫的結果,這個屬性的特點是不需要應用層返回,完全依靠協(xié)議層完成,速度快,但是寫入速度超過協(xié)議處理速度的時候,會丟包。
(6) GATT
GATT(Generic Attribute Profile):中文名叫通用屬性協(xié)議,它定義了services和characteristic兩種東西來完成低功耗藍牙設備之間的數(shù)據(jù)傳輸。它是建立在通用數(shù)據(jù)協(xié)議Attribute Protocol (ATT),之上的,ATT把services和characteristic以及相關的數(shù)據(jù)保存在一張簡單的查找表中,該表使用16-bit的id作為索引。
(7)profile
profile可以理解為一種規(guī)范,一個標準的通信協(xié)議,它存在于從機中。藍牙組織規(guī)定了一些標準的profile,例如 HID OVER GATT ,防丟器 ,心率計等。每個profile中會包含多個service,每個service代表從機的一種能力。
2. Bluetooth Socket
推薦開源項目:https://github.com/Zweo/Bluetooth (https://github.com/zolty-lionheart/Bluetooth)
以該項目demo為例介紹
藍牙端口監(jiān)聽接口和TCP端口類似:Socket和ServerSocket類。在服務器端,使用BluetoothServerSocket類來創(chuàng)建一個 監(jiān)聽服務端口。當一個連接被BluetoothServerSocket所接受,它會返回一個新的BluetoothSocket來管理該連接。在客戶 端,使用一個單獨的BluetoothSocket類去初始化一個外接連接和管理該連接。
最通常使用的藍牙端口是RFCOMM,它是被Android API支持的類型。RFCOMM是一個面向連接,通過藍牙模塊進行的數(shù)據(jù)流傳輸方式,它也被稱為串行端口規(guī)范(Serial Port Profile,SPP)。
為了創(chuàng)建一個BluetoothSocket去連接到一個已知設備,使用方法 BluetoothDevice.createRfcommSocketToServiceRecord()。然后調用connect()方法去嘗試一個 面向遠程設備的連接。這個調用將被阻塞指導一個連接已經(jīng)建立或者該鏈接失效。
為了創(chuàng)建一個BluetoothSocket作為服務端(或者“主機”),查看BluetoothServerSocket文檔。
每當該端口連接成功,無論它初始化為客戶端,或者被接受作為服務器端,通過getInputStream()和getOutputStream()來打開IO流,從而獲得各自的InputStream和OutputStream對象
BluetoothSocket類線程安全。特別的,close()方法總會馬上放棄外界操作并關閉服務器端口。
注意:需要BLUETOOTH權限。
2.1 Server端
private static final String UUIDString = "00001101-0000-1000-8000-00805F9B34FB"; //開啟服務器 private class ServerThread extends Thread { @Override public void run() { try { /* 創(chuàng)建一個藍牙服務器 * 參數(shù)分別:服務器名稱、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); //啟動接受數(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)建一個Socket連接:只需要服務器在注冊時的UUID號 BluetoothDevice device = bluetoothAdapter.getRemoteDevice(remoteAddress); BluetoothSocket socket = device.createRfcommSocketToServiceRecord(UUID.fromString(UUIDString)); //連接 Message msg2 = new Message(); msg2.obj = "請稍候,正在連接服務器:" + 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); //啟動接受數(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 = "連接服務端異常!斷開連接重新試一試。"+e.getMessage(); msg.what = CONNECT_SERVER_ERROR; linkDetectedHandler.sendMessage(msg); } } }
3. Bluetooth GATT
推薦開源項目:https://github.com/dingpwen/bl_communication (https://github.com/zolty-lionheart/bl_communication)
以該項目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); } }
參考文獻
1.Android Phone藍牙通信方式總結(Socket與Gatt)
2.Bluetooth之BluetoothSocket
3.全面且簡單明了的藍牙服務及UUID介紹
4.Android BLE藍牙開發(fā)-讀寫數(shù)據(jù) 獲取UUID
到此這篇關于詳解Android 藍牙通信方式總結的文章就介紹到這了,更多相關Android 藍牙通信內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
安卓(Android)動態(tài)創(chuàng)建多個按鈕并添加監(jiān)聽事件
本文主要介紹Android動態(tài)創(chuàng)建多個按鈕并給每個按鍵添加監(jiān)聽事件,在做Android項目會經(jīng)常遇到的,希望對需要用到的同學有所幫助2016-07-07Android Studio真機無線連接USB設備調試運行詳解流程
你在Android Studio寫app時是否也有想過如果可以不用數(shù)據(jù)線連接手機調試運行就好了?如果需要取出數(shù)據(jù)線插接的話我肯定是嫌麻煩的,但是模擬器有時候需要測試一些需要硬件支持的功能時又不管用,所以最好的測試還是在真機上,本篇教你扔掉數(shù)據(jù)線來無線調試2021-11-11Android intent之間復雜參數(shù)傳遞方法詳解
這篇文章主要介紹了Android intent之間復雜參數(shù)傳遞方法,較為詳細的分析了Android中intent參數(shù)傳遞的常見方法與使用技巧,需要的朋友可以參考下2016-10-10Android?配合Mat工具監(jiān)聽查找內存泄漏的操作方法
這篇文章主要介紹了Android?配合Mat工具監(jiān)聽查找內存泄漏問題,使用Android Studio Profiler查看內存的操作,本文通過圖文實例相結合給大家介紹的非常詳細,需要的朋友可以參考下2022-05-05Android引用開源框架通過AsyncHttpClient實現(xiàn)文件上傳
這篇文章主要介紹了Android引用開源框架通過AsyncHttpClient實現(xiàn)文件上傳,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-01-01