JavaSE + bluecove 蓝牙连接

2023-10-31

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JavaSE + bluecove 蓝牙连接 的相关文章

随机推荐

  • 使用scrapy和selenium结合爬取网易新闻内容

    代码结构 相关文件的代码 爬虫文件 mid py 爬虫文件 import scrapy from middle items import MiddleItem from selenium import webdriver class Mid
  • 欧拉降幂公式

    欧拉降幂公式 a b a b equiv ab a b
  • CVE-2022-24112 Apache APISIX 命令执行漏洞复现

    CVE 2022 24112 Apache APISIX 命令执行漏洞 Apache APISIX 是 Apache 软件基金会下的云原生 API 网关 它兼具动态 实时 高性能等特点 提供了负载均衡 动态上游 灰度发布 金丝雀发布 服务熔
  • 如何去编写一个C++程序

    如何去编写一个C 程序 1 防卫式声明 2 写class的头 3 考虑复数准备什么数据 4 考虑复数准备哪些函数 5 类外定义函数 成员函数 6 类外定义函数 非成员函数即全局函数 7 调用函数 学习侯捷老师讲授课程C 面向对象高级开发 总
  • 在浏览器中输入URL中会发生什么

    面试官问 在浏览器中输入URL中会发生什么 我们首先分析下这个问题 这是一个很宽泛 细节非常非常多的一个问题 如果要展开来细说我们可以直接从应用层的协议 讲到传输层 网络层 再到数据链路层 可是我们这里不建议大家一开始就深挖协议中的细节 原
  • Unity 视频播放

    Unity 视频播放 前言 在Unity引擎做视频播放的方式有很多种 这里介绍两种 一种是使用RawImage组件的纹理进行视频播放 将视频每一帧的画面复制在RawImage的纹理贴图中 实现视频的播放展示 另外一种是使用插件Av Pro进
  • node.js系统学习4-sync&&async

    async异步 sync同步 阻塞 非阻塞 https www runoob com nodejs nodejs callback html
  • PASCAL VOC 2012 数据集解析

    目录 一 Introduction Classification Detection Competitions Segmentation Competition Action Classification Competition Image
  • IDEA创建第一个spring boot项目提示cannot resolve xxx等错误

    在学习spring boot课程 精通spring boot 42讲 时 根据课程安装好maven和jdk之后 准备开始使用IDEA创建第一个spring boot项目 使用插件spring assistant创建好项目之后 却发现包org
  • python爬虫时报错

    我用的是Python3 66版本 最近写了一段爬虫代码 在DEBUFG模式运行时没有报错 但是普通模式运行时就报下面这个错误 wrap socket argument 1 must be socket socket not SSLSocke
  • (附源码)springboot垃圾自动分类管理系统 毕业设计 160846

    摘 要 随着现在网络的快速发展 网络的应用在各行各业当中它很快融入到了许多分类管理之中 他们利用网络来做这个垃圾自动分类的网站 随之就产生了 垃圾自动分类管理系统 这样就让垃圾自动分类管理系统更加方便简单 对于本垃圾自动分类管理系统的设计来
  • Matlab绘图?学会一招就够了——源代码

    hello 欢迎来到我的博客 你应该是b站来的小伙伴吧 谢谢你的关注 这篇博客的内容是以下这个视频的源代码 Matlab绘图 学会一招就够了 代码如下 clc clear all close all 生成图窗 clc 清楚command窗口
  • 双向卷积神经网络_基于双向特征融合卷积神经网络的液晶面板缺陷检测算法

    基于双向特征融合卷积神经网络的液晶面板缺陷检测算法 彭大芹 刘恒 许国良 邓柯 摘 要 摘要 针对手机液晶面板生产工业中缺陷检测面临的精度低的问题 提 出了一种基于深度学习的液晶面板缺陷检测算法 该算法在传统单向特征融合 的基础上提出了双向
  • Windows上运行Caffe自带的mnist例子

    环境要求 win10 VS2013 CUDA7 5 Caffe的windows版下载地址 https github com Microsoft caffe 配置Caffe工程 1 将Caffe源码的windows目录下CommonSetti
  • 第十三届蓝桥杯大赛软件赛决赛(Java 大学A组)

    蓝桥杯 2022年国赛真题 Java 大学A组 试题 A 火柴棒数字 试题 B 小蓝与钥匙 试题 C 内存空间 试题 D 斐波那契数组 试题 E 交通信号 试题 F 数组个数 试题 G 六六大顺 试题 H 选素数 试题 I 图书借阅 试题
  • GFS浅谈

    GFS Google File System Big Table Map Reduce作为google的三宝技术 是Google诸多服务的基石 我想就自己的理解对GFS文件系统作一个分析 GFS首先是属于分布文件系统 但作为Google公司
  • PAT C入门题目练习-7-90 螺旋方阵 (20 分)

    7 90 螺旋方阵 20 分 所谓 螺旋方阵 是指对任意给定的N 将1到N N的数字从左上角第1个格子开始 按顺时针螺旋方向顺序填入N N的方阵里 本题要求构造这样的螺旋方阵 输入格式 输入在一行中给出一个正整数N lt 10 输出格式 输
  • C#中的一些基本方式总结

    目录 1 委托delegate c 中的将方法作为参数传递 2 堆和栈 3 override重写 4 new关键字 如果子类声明了和父类同样的方法 但用new声明了 会隐藏掉父类的方法 5this和base 6 sealed类 7 泛型方法
  • python中的sort排序_python的sort()怎么排序

    python中的sort 函数只能应用在列表list上 而sorted可以对所有可迭代的对象进行排序的操作 sort方法会在原list上直接进行排序 不会创建新的list 而sorted方法不会对原来的数据做任何改动 排序后的结果是新生成的
  • JavaSE + bluecove 蓝牙连接

    最近公司准备将旧系统的 NET部分翻版 项目除了有后台的还有个与设备对接的客户端用蓝牙连接的 所有这周对相关技术做了一个验证 搜了一下Java 蓝牙相关信息 我去资料也太少了 少也就算了连bluecove库也是有问题的 经过艰难的查找 最终