藍牙作為一種短距離無線通訊技術,越來越融合到我們的生活當中,軟件開發也是隨處可見。本文介紹的是PC端與其他藍牙設備建立通訊的一個案例。
藍牙客戶端,即是請求連接的一端,搜索會發現可連接的藍牙設備名稱。
藍牙服務端,提供服務的一端。往往生活中使用到的藍牙(比如手機是個雙重角色),又是客戶端又是服務端。
藍牙通信的原理
藍牙技術規定每一對設備之間進行藍牙通訊時,必須一個為主角色,另一為從角色,才能進行通信,通信時,必須由主端進行查找,發起配對,建鏈成功后,雙方即可收發數據。藍牙主端設備發起呼叫,首先是查找,找出周圍處於可被查找的藍牙設備。主端設備找到從端藍牙設備后,與從端藍牙設備進行配對,此時需要輸入從端設備的PIN碼,也有設備不需要輸入PIN碼。
藍牙流程
藍牙客戶端Socket的與Sokcet流程是一樣的,只不過參數不同而已。如下:
1、創建客戶端藍牙Sokcet
2、創建連接
3、讀寫數據
4、關閉
因此又可以視為一個短連接。
一、jar包
首先,在PC端實現藍牙java並沒有提供官方jar包,本文使用的是一個第三方庫。需要手動導入到maven倉庫,
不會導倉庫的請參考https://www.cnblogs.com/zeussbook/p/9930679.htm。
第三方jar包下載地址:https://sourceforge.net/projects/bluecove/files/BlueCove/2.1.0/bluecove-2.1.0.jar/download
二、代碼部分
2.1藍牙服務端
package com.shunyutong.test.bluetooth; import javax.bluetooth.DiscoveryAgent; import javax.bluetooth.LocalDevice; import javax.microedition.io.Connector; import javax.microedition.io.StreamConnection; import javax.microedition.io.StreamConnectionNotifier; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @Auther: lanhaifeng * @Date: 2020/5/4 0004 16:28 * @Description: 藍牙服務端 * @statement: */ public class BluetoothServer implements Runnable{ //本機藍牙設備 private LocalDevice local = null; // 流連接 private StreamConnection streamConnection = null; // 輸入流 private InputStream inputStream; private OutputStream outputStream; //接入通知 private StreamConnectionNotifier notifier; //基於緩存的線程池 private final static ExecutorService service = Executors.newCachedThreadPool(); public String serverName; public String serverUUID; private OnServerListener mServerListener; /** * 服務監聽接口 */ public interface OnServerListener { void onConnected(InputStream inputStream, OutputStream outputStream); void onDisconnected(); void onClose(); } /** * 藍牙服務有參構造函數 * @param serverUUID id * @param serverName 名稱 */ public BluetoothServer(String serverUUID, String serverName) { this.serverUUID = serverUUID; this.serverName = serverName; } /** * 啟動 */ public void start() { try { local = LocalDevice.getLocalDevice(); if (!local.setDiscoverable(DiscoveryAgent.GIAC)) System.out.println("請將藍牙設置為可被發現"); //作為服務端,被請求 String url = "btspp://localhost:" + serverUUID+ ";name="+serverName; notifier = (StreamConnectionNotifier) Connector.open(url); service.submit(this); } catch (IOException e) { System.out.println(e.getMessage());; } } /** * 重寫run() */ @Override public void run() { try { streamConnection = notifier.acceptAndOpen(); //阻塞的,等待設備連接 inputStream = streamConnection.openInputStream(); outputStream = streamConnection.openOutputStream(); if (mServerListener != null) { mServerListener.onConnected(inputStream, outputStream); } } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { } } public OnServerListener getServerListener() { return mServerListener; } public void setServerListener(OnServerListener mServerListener) { this.mServerListener = mServerListener; } }
2.2藍牙客戶端
package com.shunyutong.test.bluetooth; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Iterator; import java.util.Set; import javax.bluetooth.RemoteDevice; import javax.microedition.io.Connector; import javax.microedition.io.StreamConnection; /** * @Auther: lanhaifeng * @Date: 2020/5/4 0004 15:37 * @Description:藍牙客戶端類 * @statement: */ public class BluetoothClient { private StreamConnection streamConnection;//流連接 private OnDiscoverListener onDiscoverListener = null;//發現監聽 private OnClientListener onClientListener = null;//客戶端監聽 /** * 客戶端監聽 */ public interface OnClientListener { void onConnected(InputStream inputStream, OutputStream outputStream); void onConnectionFailed(); void onDisconnected(); void onClose(); } /** * 發現監聽 */ public interface OnDiscoverListener { void onDiscover(RemoteDevice remoteDevice); } /** * 無參構造函數 */ public BluetoothClient() { } /** * 查找所有 * @throws IOException * @throws InterruptedException */ public void find() throws IOException, InterruptedException { //附近所有的藍牙設備,必須先執行 runDiscovery Set<RemoteDevice> devicesDiscovered = RemoteDeviceDiscovery.getDevices(); Iterator<RemoteDevice> itr = devicesDiscovered.iterator(); //連接 while (itr.hasNext()) { RemoteDevice remoteDevice = itr.next(); onDiscoverListener.onDiscover(remoteDevice); } } /** * 啟動連接 * @param remoteDevice * @param serviceUUID * @throws IOException * @throws InterruptedException */ public void startClient(RemoteDevice remoteDevice, String serviceUUID) throws IOException, InterruptedException { String url = RemoteDeviceDiscovery.searchService(remoteDevice, serviceUUID); streamConnection = (StreamConnection) Connector.open(url); if (this.onClientListener != null) { this.onClientListener.onConnected(streamConnection.openInputStream(), streamConnection.openOutputStream()); } } public OnDiscoverListener getOnDiscoverListener() { return onDiscoverListener; } public void setOnDiscoverListener(OnDiscoverListener onDiscoverListener) { this.onDiscoverListener = onDiscoverListener; } public OnClientListener getClientListener() { return onClientListener; } public void setClientListener(OnClientListener onClientListener) { this.onClientListener = onClientListener; } }
2.3設備查找類
package com.shunyutong.test.bluetooth; import java.io.IOException; import java.util.HashSet; import java.util.Set; import java.util.Vector; import javax.bluetooth.DataElement; import javax.bluetooth.DeviceClass; import javax.bluetooth.DiscoveryAgent; import javax.bluetooth.DiscoveryListener; import javax.bluetooth.LocalDevice; import javax.bluetooth.RemoteDevice; import javax.bluetooth.ServiceRecord; import javax.bluetooth.UUID; /** * @Auther: lanhaifeng * @Date: 2020/5/4 0004 16:07 * @Description:設備查找類 * @statement: */ public class RemoteDeviceDiscovery { public final static Set<RemoteDevice> devicesDiscovered = new HashSet<RemoteDevice>(); public final static Vector<String> serviceFound = new Vector<String>(); final static Object serviceSearchCompletedEvent = new Object(); final static Object inquiryCompletedEvent = new Object(); /** * 發現監聽 */ private static DiscoveryListener listener = new DiscoveryListener() { public void inquiryCompleted(int discType) { System.out.println("#" + "搜索完成"); synchronized (inquiryCompletedEvent) { inquiryCompletedEvent.notifyAll(); } } /** * 發現設備 * @param remoteDevice * @param deviceClass */ @Override public void deviceDiscovered(RemoteDevice remoteDevice, DeviceClass deviceClass) { devicesDiscovered.add(remoteDevice); try { System.out.println("#發現設備" + remoteDevice.getFriendlyName(false)); } catch (IOException e) { e.printStackTrace(); } } /** * 發現服務 * @param transID id * @param servRecord 服務記錄 */ @Override public void servicesDiscovered(int transID, ServiceRecord[] servRecord) { for (int i = 0; i < servRecord.length; i++) { String url = servRecord[i].getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false); if (url == null) { continue; } serviceFound.add(url); DataElement serviceName = servRecord[i].getAttributeValue(0x0100); if (serviceName != null) { System.out.println("service " + serviceName.getValue() + " found " + url); } else { System.out.println("service found " + url); } } System.out.println("#" + "servicesDiscovered"); } /** * 服務搜索已完成 * @param arg0 * @param arg1 */ @Override public void serviceSearchCompleted(int arg0, int arg1) { System.out.println("#" + "serviceSearchCompleted"); synchronized(serviceSearchCompletedEvent){ serviceSearchCompletedEvent.notifyAll(); } } }; /** * 查找設備 * @throws IOException * @throws InterruptedException */ private static void findDevices() throws IOException, InterruptedException { devicesDiscovered.clear(); synchronized (inquiryCompletedEvent) { LocalDevice ld = LocalDevice.getLocalDevice(); System.out.println("#本機藍牙名稱:" + ld.getFriendlyName()); boolean started = LocalDevice.getLocalDevice().getDiscoveryAgent().startInquiry(DiscoveryAgent.GIAC,listener); if (started) { System.out.println("#" + "等待搜索完成..."); inquiryCompletedEvent.wait(); LocalDevice.getLocalDevice().getDiscoveryAgent().cancelInquiry(listener); System.out.println("#發現設備數量:" + devicesDiscovered.size()); } } } /** * 獲取設備 * @return * @throws IOException * @throws InterruptedException */ public static Set<RemoteDevice> getDevices() throws IOException, InterruptedException { findDevices(); return devicesDiscovered; } /** * 查找服務 * @param btDevice * @param serviceUUID * @return * @throws IOException * @throws InterruptedException */ public static String searchService(RemoteDevice btDevice, String serviceUUID) throws IOException, InterruptedException { UUID[] searchUuidSet = new UUID[] { new UUID(serviceUUID, false) }; int[] attrIDs = new int[] { 0x0100 // Service name }; synchronized(serviceSearchCompletedEvent) { System.out.println("search services on " + btDevice.getBluetoothAddress() + " " + btDevice.getFriendlyName(false)); LocalDevice.getLocalDevice().getDiscoveryAgent().searchServices(attrIDs, searchUuidSet, btDevice, listener); serviceSearchCompletedEvent.wait(); } if (serviceFound.size() > 0) { return serviceFound.elementAt(0); } else { return ""; } } }
2.4藍牙服務端使用
package com.shunyutong.test.bluetoothService; import com.shunyutong.test.bluetooth.BluetoothServer; import java.io.InputStream; import java.io.OutputStream; /** * @Auther: lanhaifeng * @Date: 2020/5/4 0004 16:47 * @Description: 藍牙服務端通信業務 * @statement: */ public class BluetoothServerService { public static void main(String[] argv) { final String serverName = "Bluetooth Server Test"; final String serverUUID = "1000110100001000800000805F9B34FB"; //根據需要自定義 BluetoothServer server = new BluetoothServer(serverUUID, serverName); server.setServerListener(new BluetoothServer.OnServerListener() { @Override public void onConnected(InputStream inputStream, OutputStream outputStream) { System.out.printf("Connected"); //添加通信代碼 System.out.println(server.serverName); System.out.println(server.serverUUID); } @Override public void onDisconnected() { } @Override public void onClose() { } }); server.start(); } }
2.5藍牙客戶端使用
package com.shunyutong.test.bluetoothService; import com.shunyutong.test.bluetooth.BluetoothClient; import javax.bluetooth.RemoteDevice; import javax.microedition.io.ConnectionNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Vector; /** * @Auther: lanhaifeng * @Date: 2020/5/4 0004 16:51 * @Description: 藍牙客戶端業務類 * @statement: */ public class BluetoothClientService { public static void main(String[] argv) { final String serverUUID = "1000110100001000800000805F9B34FB"; //需要與服務端相同 BluetoothClient client = new BluetoothClient(); Vector<RemoteDevice> remoteDevices = new Vector<>(); client.setOnDiscoverListener(new BluetoothClient.OnDiscoverListener() { @Override public void onDiscover(RemoteDevice remoteDevice) { remoteDevices.add(remoteDevice); } }); client.setClientListener(new BluetoothClient.OnClientListener() { @Override public void onConnected(InputStream inputStream, OutputStream outputStream) { System.out.printf("Connected"); //添加通信代碼 } @Override public void onConnectionFailed() { System.out.printf("Connection failed"); } @Override public void onDisconnected() { } @Override public void onClose() { } }); try { client.find(); if (remoteDevices.size() > 0 ) { client.startClient(remoteDevices.firstElement(), serverUUID); } }catch (InterruptedException e) { e.printStackTrace(); }catch (ConnectionNotFoundException e){ System.out.println("當前藍牙不在線"); e.printStackTrace(); }catch (IOException e) { e.printStackTrace(); } } }
測試效果

