JavaSE + bluecove 藍芽連線
最近公司準備將舊系統的.NET部分翻版,專案除了有後臺的還有個與裝置對接的客戶端用藍芽連線的,所有這周對相關技術做了一個驗證。
搜了一下Java 藍芽相關資訊,我去資料也太少了,少也就算了連bluecove庫也是有問題的。經過艱難的查詢,最終還是調通了。因為整個過程都是靠大家的資料去解決的,所以本著造福後人的角度我把我的經驗分享一下,讓後人不用想我一樣滿世界去找。
BlueCove框架
首先是Java SE雖然支援藍芽但是支援的並不是很好,反而在Java ME 支援裝置方面較好,但是我桌面平臺也要啊,所有就有框架BlueCove了。BlueCove的API我用了感覺風格整體都和Java ME差不多,所以當你有比較複雜的需求又找不到文件的時候可以參考一下Java ME的資料
BlueCove框架最大的問題就是比較老了, 2.1.1-SNAPSHOT.63 文件最後更新是2010年,提供的jar包居然不支援64位系統!還好有解決方案的,不然我就不用開發了。這裡首先參考這篇部落格 Eclipse + Java + BlueCove + WIN/MAC 藍芽開發 這篇對解決方法的介紹的比較詳細,但是並沒有幫我直接解決問題,因為我順著連結去下載的jar還是不支援64位系統(也可能是我下錯了包了)。不過我知道了是可以解決的,於是各種百度和谷歌終於找到了能在64位系統執行的jar包。
64位系無法執行會提示:
Native Library intelbth_x64 not available
Native Library bluecove_x64 not available
官網直接下載的2.1.0版本不支援64位,下面這個支援的版本看路徑也是官方的,竟然不提供!虧我找了不知道多久。
64位支援版本jar包下載地址:
BlueCove還需要Apache的commons-io包,這個順便下就可以的。
測試環境:
藍芽是與網絡卡一體的 bcm94352
藍芽連線:作為服務端
這三篇都是將PC作為一個服務端,讓手機主動連線。
//本機藍芽裝置
private LocalDevice local = null;
// 流連線
private StreamConnection streamConnection = null;
// 接受資料的位元組流
private byte[] acceptdByteArray = new byte[1024];
// 輸入流
private DataInputStream inputStream;
//接入通知
private StreamConnectionNotifier notifier;
//執行緒池
private final static ExecutorService service = Executors.newCachedThreadPool();
public BuletoothService() {
try {
//這兩步不一定要
BluCatUtil.doctorDevice(); // 驅動檢查
RemoteDeviceDiscovery.runDiscovery(); // 搜尋附近所有的藍芽裝置
System.out.println(RemoteDeviceDiscovery.getDevices());
} catch (IOException | InterruptedException e1) {
e1.printStackTrace();
}
try {
local = LocalDevice.getLocalDevice();
if (!local.setDiscoverable(DiscoveryAgent.GIAC))
System.out.println("請將藍芽設定為可被發現");
/**
* 作為服務端,被請求
*/
String url = "btspp://localhost:" + new UUID(80087355).toString()
+ ";name=RemoteBluetooth";
notifier = (StreamConnectionNotifier)Connector.open(url);
} catch (IOException e) {
e.printStackTrace();
}
service.submit(this);
}
上面的其實就一個Connector.open(url)
是重點,service.submit(this);
獲取資料在新執行緒中。
為了能夠手動停止執行緒,做了一些處理。
@Override
public void run() {
try {
String inStr = null;
streamConnection = notifier.acceptAndOpen(); //阻塞的,等待裝置連線
inputStream = streamConnection.openDataInputStream();
int length;
while (true) {
if ((inputStream.available()) <= 0) { //不阻塞執行緒
if (stopFlag) //UI停止後,關閉
break;
Thread.sleep(800); //資料間隔比較長,手動堵塞執行緒
} else {
length = inputStream.read(acceptdByteArray);
if(length>0) {
inStr = new String(acceptdByteArray,0,length);
System.out.println(inStr);
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
try {
if (inputStream != null)
inputStream.close();
if (streamConnection != null)
streamConnection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
核心就是streamConnection = notifier.acceptAndOpen();
獲得連線這裡會堵塞執行緒(沒有作停止處理!!!)。有連線後獲得輸入流inputStream = streamConnection.openDataInputStream();
接下來就是你自己發揮了。
藍芽連線:作為客戶端
網上關於這個的是真的幾乎沒有!因為需要一個服務端,現在我手頭客戶端是挺多的就是缺個服務端。公司的電腦都是沒藍芽的,過幾天問同事借太筆記本再測試!所以下面的程式碼我也只是提供一個思路,具體你可以測試來告訴我。或者我測試通過再告訴你們。整體都是差不多的,先連線服務端,再獲取輸入流/輸出流。這裡寫了一個工具類先查詢了附近的藍芽裝置(詳見本文藍芽發現小節),因為需要 BluetoothAddress
,如果這個是已知的可以直接寫。
Set<RemoteDevice> devicesDiscovered = RemoteDeviceDiscovery.getDevices(); //附近所有的藍芽裝置,必須先執行 runDiscovery
if (devicesDiscovered.iterator().hasNext()) { //連線
RemoteDevice first = devicesDiscovered.iterator().next();
streamConnection = (StreamConnection) Connector.open("btspp://" + first.getBluetoothAddress() + ":1");
}
Set<RemoteDevice> devicesDiscovered = RemoteDeviceDiscovery.getDevices()
附近可用的藍芽連線streamConnection = (StreamConnection) Connector.open("btspp://" + first.getBluetoothAddress() + ":1");
直接連線- url地址 :
btspp://<藍芽裝置地址>:<通道號>
- 有連線後獲得輸入流
inputStream = streamConnection.openDataInputStream();
一樣了
有個簡單參考部落格:http://blog.sina.com.cn/s/blog_a861feb40102vppo.html
裡面沒有太多的內容,流程比較清晰。通道號我用pc連線Anrdoid手機的時候,好像不同的通道有不同的功能,有的提示要讀取音訊,有的提示要讀取電話和聯絡人等。可能這個通道在PC上功能又是不一樣的。
2017.8.22 PC讀取藍芽連線的電子秤資料驗證通過,客戶端可行。
藍芽裝置發現
搜尋周邊可用的藍芽是比較方便的。
public final static Set<RemoteDevice> devicesDiscovered = new HashSet<RemoteDevice>();
private static void findDevices() throws IOException, InterruptedException {
final Object inquiryCompletedEvent = new Object();
devicesDiscovered.clear();
DiscoveryListener listener = new DiscoveryListener() {
public void inquiryCompleted(int discType) {
System.out.println("#" + "搜尋完成");
synchronized (inquiryCompletedEvent) {
inquiryCompletedEvent.notifyAll();
}
}
@Override
public void deviceDiscovered(RemoteDevice remoteDevice, DeviceClass deviceClass) {
devicesDiscovered.add(remoteDevice);
try {
System.out.println("#發現裝置" + remoteDevice.getFriendlyName(false));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void servicesDiscovered(int arg0, ServiceRecord[] arg1) {
System.out.println("#" + "servicesDiscovered");
}
@Override
public void serviceSearchCompleted(int arg0, int arg1) {
System.out.println("#" + "serviceSearchCompleted");
}
};
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());
}
}
}
可以參考Java ME的流程,核心是:
LocalDevice.getLocalDevice().getDiscoveryAgent().startInquiry(DiscoveryAgent.GIAC,listener);
(裝置可被發現,搜尋監聽器),監聽器在不同的過程節點提供了回撥方法,裝置發現的時候新增進集合中:
@Override
public void deviceDiscovered(RemoteDevice remoteDevice, DeviceClass deviceClass) {
devicesDiscovered.add(remoteDevice);
try {
System.out.println("#發現裝置" + remoteDevice.getFriendlyName(false));
} catch (IOException e) {
e.printStackTrace();
}
}
擴充套件參考
感謝
Demo下載
沒有程式碼總是很難解決一些問題,