JavaSE + bluecove 蓝牙连接

java 专栏收录该内容
1 篇文章 0 订阅

最近公司准备将旧系统的.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包下载地址:

http://snapshot.bluecove.org/distribution/download/2.1.1-SNAPSHOT/2.1.1-SNAPSHOT.63/

老铁们,这个地址访问不了直接看文章最后的Demo,里面有lib包

BlueCove还需要Apache的commons-io包,这个顺便下就可以的。

测试环境:
系版本
蓝牙是与网卡一体的 bcm94352

蓝牙连接:作为服务端

解决了框架问题接下去就简单了,这方面网上的例子不多但还是有的(都差不多),都是手机控制电脑,这几个而是从上面那个博客找到的(感谢上一个博客让我少走了一点弯路)。
参考以下三篇文章:
http://royal2xiaose.iteye.com/blog/1420138
http://www.eoeandroid.com/thread-264135-1-1.html
http://blog.csdn.net/pku_android/article/details/7430849

这三篇都是将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");
			}
  1. Set<RemoteDevice> devicesDiscovered = RemoteDeviceDiscovery.getDevices()附近可用的蓝牙连接
  2. streamConnection = (StreamConnection) Connector.open("btspp://" + first.getBluetoothAddress() + ":1"); 直接连接
  3. url地址 :btspp://<蓝牙设备地址>:<通道号>
  4. 有连接后获得输入流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();
	}			
}

扩展参考

如果有更多的需求有以下几个文档可以参考:
官方API文档: http://snapshot.bluecove.org/
Java ME 蓝牙:http://www.oracle.com/technetwork/java/javame/tech/index-140411.html

感谢

http://blog.csdn.net/old_me_mory/article/details/18962701作者:Old_Me_Mory 提供了我总的解决思路;
还有许多的文章作者提供了解决思路!

Demo下载

没有代码总是很难解决一些问题,点击这里Demo下载

打赏
文章很值,打赏犒劳作者一下
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页

打赏

Svizzera

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值