手机蓝牙编程简介
一、什么是蓝牙技术
蓝牙是一种低成本、短距离的无线通信技术。对于那些希望创建个人局域网(PANs )的人们来说,蓝牙技术已经越来越流行了。每个个人局域网都在独立设备的周围被动态地创建,并且为蜂窝 式电话和PDA 等设备提供了自动连接和即时共享数据的能力。为了在Java 平台上开发支持蓝牙技术的软件,JCP 定义了JSR82 标准--Java 蓝牙无线技术APIs(JABWT) 。
当蓝牙设备互相连接时,他们将组成一个微微网(piconet ), 即以一个主设备和最大7 个从设备的形式动态创建网络。蓝牙也支持piconet 网之间的连接:当一个piconet 中的 主设备成为另一个piconet 的从设备时,piconet 与piconet 间将形成桥接。
二、蓝牙协 议栈
蓝牙协议栈允许采用 多种方法,包括 RFCOMM 和 Object Exchange (OBEX ), 在设备之间发送和接收文件。如果想发送和接收流数据(而且想采用传统的串口应用程序,并给它加上蓝牙支持),那么 RFCOMM 更好。反过来,如果想发送对象数据以及关于负载的上下文和元数据,则 OBEX 最好。图 1 显示了协议栈的细节。
图1 蓝牙协议栈 ,如下 :
三、蓝牙规范用例
初始化-- 所有具备蓝牙功能的应用程序必须先要初始化蓝牙 栈。
服 务器:建立一个服务,然后等待一个客户端来连接。
客 户端:搜索服务,然后尝试与服务器建立连接。
图2 一个蓝牙规范用例图,如下:
四、用例中参与活动的图表
图3 蓝牙应用程序活动图,如下:
五、JSR-82 API 简介
JSR-82 是用于蓝牙无线技术的官方Java API 。可使用这个API 创建可执行以下功能 的应用程序:
JSR-82 包含两个包,即javax.bluetooth 和javax.obex 。
图4 显示了在MIDlet 中一个典型蓝牙功能应用程序中的 一些元素,如下:
1 、本地设备类:
图5 :LocalDevice 类
本地设备提供了方法来返回关于本地设备的信息,并且能够进入 Bluetooth manager :
.getBluetoothAddress() 返回蓝牙设备地址。
.getDeviceClass() 返回设备类。
.getFriendlyName() 返回设备友好名称,蓝牙设备名通常是用户在蓝牙控制中心为其设置的我们将会在后面看到。
.getRecord() 返回一个指定蓝牙连接的服务记录。
.updateRecord() 方法用来为指定的ServiceRecord 更 新SDDB 服务记录。
.getDiscoverable() 返回设备的可发现状态。
.setDiscoverable() 设置设备的可发现状态。
.getDiscoveryAgent() 返回一个参考给发现代理。
.getProperty() 返回一个设备的蓝牙属性
通过 调用getProperty() 方法你可以得到的属性包括:
.bluetooth.api.version ,蓝牙API 版本
.bluetooth.sd.attr.retrievable.max ,一次性能够被获得的服务记录属性的最大值
.bluetooth.connected.devices.max ,支持的连接设备的最大值
.bluetooth.sd.trans.max ,同时发生的服务发现处理的最大值
.bluetooth.l2cap.receiveMTU.max ,L2CAP 最 大发射单元
你可以在Javadoc 文档中或是规范中学习更多的有 关蓝牙属性的内容。
2 、远端设备类:
图6 :RemoteDevice 类
远端设备(RemoteDevice ) 提供的方法中,有些很类似于本地设备 (LocalDevice )里提供的方法:
.getBluetoothAddress() 返回蓝牙地址。
.getFriendlyName() 返回蓝牙设备名。
.getRemoteDevice() 返回相应的被指定蓝牙连接的远端设备。
.authenticate() 尝试识别验证远端设备。
.authorize() 为指定的蓝牙连接去尝试批准远端设备访问本地设备。
.encrypt() 尝试为指定的蓝牙连接开启或关闭加密。
.isAuthenticated() 测试是否远端设备可以被验证。
.isAuthorized() 测试是否远端设备已经被蓝牙控制中心授权访问本地设备以进行蓝牙连接。
.isEncrypted() 测试是否本地设备和远端设备之间的通信被加密。
.isTrustedDevice() 测试是否远端设备被蓝牙控制中心指定为可信任的。
3 、DeviceClass 类
一个DeviceClass 对象代表一个设备的设备类(CoD ), 例如一个打印机或者一部电话。CoD 包括一个主类,一个辅的类,和服务类型或服务类。 DeviceClass 提供了如下方法:
.getMajorDeviceClass() 方法获取设备的主类。
.getMinorDeviceClass() 方法获取设备的辅类。
.getServiceClasses() 获取设备的服务类。
当 一个设备被发现,同时他的类也会被发现;当发现代理调用deviceDiscovered() 时, 其中一个参数就是DeviceClass 。你可以通过它 的getDeviceClass() 方 法找到本地设备的CoD 。
4 、DiscoveryAgent 类是个有帮助的类,它让您可以发现附近的远程蓝牙设备,并为 区域内的每个蓝牙设备返回一个 RemoteDevice 。也可以使用javax.bluetooth.DiscoveryAgent 在已经发现的远程设备上搜索服务。如果想在发生发现事件的时候得到通知,则需要实现 DiscoveryListener 接口的方 法。见图7DiscoveryAgent 类和DiscoveryListener 接 口,如下:
5 、设备发现API
你使用DiscoveryAgent 类的" 设备发现" 方法来开始和取消设备发现:
.retrieveDevices() 重新获得已经发现或者附近的已知设备
.startInquiry() 启动发现附近设备,也叫inquiry
.cancelInquiry() 取消当前进行的任何请求
蓝牙发现代理在请求阶段的不同时候会分别调用DiscoveryListener (发 现监听器)不同的回调方法:
.deviceDiscovered() 指出是否有设备被发现。
.inquiryCompleted() 指出是否请求已经成功、触发一个错误或已被取消。
在图8 中的状态图表阐明了设备发现的状态改变结束于相应的回调方法的返回。
图 8: 设备发现状态表
设备发现以调用startInquiry() 函数开始。在请求进行时,蓝牙发现代理会在适当的时候调用回调方法DeviceDiscovered() 和 inquiryCompleted() 。
6 、服务发现API
你可以使用发现代理的服务发现方法来开始或取消服务发现:
. selectService() 启动服务发现搜索。(根据API 手册应为尝试定 位一个服务)
. searchServices() 启动服务发现搜索。
. cancelServiceSearch() 取消在正在进行中的任何的服务发现搜索操作。蓝牙发现代理在服务发现阶段的不同时候会分别 调用 DiscoveryListener 的服务发现回调方法:
. servicesDiscovered() 表示是否服务已被发现。
. serviceSearchCompleted() 表示服务发现是否已经完成。
图9 阐明了服务发现的状态改变结束于DiscoveryListener 的 回调方法的返回。
图 9: 服务发现状态图表
服务发现开始于对searchServices() 的调用。当服务搜索进行时,蓝牙发现代理会在适当的时候回调servicesDiscovered() 和 serviceSearchCompleted() 方法。
除了DiscoveryAgent 和DiscoveryListener 了, 你在服务发现过程中还要使用到的类有UUID ,ServiceRecord 以 及DataElement 等。
7 、UUID 类
在蓝牙中,每个服务和服 务属性都唯一地由" 全球唯一标识符" (UUID )来校验。正如它的名字所暗示的,每一个这样的标识符都要在时空上保证唯一。UUID 类可表现为短整形(16 或32 位)和长整形(128 位)UUID 。他提供了分别利用String 和16 位或32 位数值来创建类的构造函数,提供了一个可以 比较两个UUID (如果两个都是128 位) 的方法,还有一个可以转换一个UUID 为一个字符串的方法。UUID 实 例是不可改变的(immutable ),只有被UUID 标 示的服务可以被发现。
在Linux 下你用一个命令uuidgen -t 可以生成一个UUID 值;在Windows 下 则执行命令uuidgen 。UUID 看起来 就像如下的这个形式:2d266186-01fb-47c2-8d9f-10b8ec891363 。 当使用生成的UUID 去创建一个 UUID 对 象,你可以去掉连字符。
8 、SDDB 和ServiceRecord 接口
在服务发现的中心是服务发现数据库(SDDB) 和服务发现协议(SDP )。SDDB 由蓝牙实现负责维护的数据库。它包含了服务记录(service records ),后者代表了对客户端有效的服务。SDP 对于基于JABWT (Java 蓝 牙无线技术APIs )应用程序来说是透明的;可以这么说,SDP 是用于服务发现的。为重新获取服务纪录,一个本地设备SDP 客 户端会向一个远端设备上SDP 服务器发出请求。
图 10: SDDB
每一笔服务记录都会由一个ServiceRecord 的 实例来表现。这个记录包含了描述服务细节的属性。这个类提供了几种有用的方法:
.getAttributeIDs() 和 getAttributeValue() 方法返回服务记录的属性。
.getConnectionURL() 方法获取链接的URL 地址给服务器 主机来收集服务记录。
.getHostDevice() 方法获取提供服务的远端设备。
.populateRecord() 和 setAttributeValue() 方法用来设置设备记录的属性。
.setDeviceServiceClasses() 方法设置服务的类。
图11 显示了蓝牙本地设备和远端设备,以及SDDB 还 有服务记录之间的关系:
图 11: 使 用远端设备,SDDB 和服务记录进行服务发现
为使服务端可以被客户端来使用,服务应用程序要通过如下方法建立一个 服务记录,首先要创建一个连接通知器(connection notifier ),然后由调用连接 通知器的acceptAndWait() 方法来向SDDB 中 插入记录。服务端程序能够在适当的时候获得记录和更新。客户端应用程序向远端SDDB 请求可以使用 的服务,会发现服务记录。
六、如何创建一个蓝牙服务端
具 体代码步骤:
1、 得到本地设备。
try {
this . localDevice = LocalDevice.getLocalDevice ();
this . localDevice .setDiscoverable(DiscoveryAgent. GIAC );
} catch (BluetoothStateException ex) {
ex.printStackTrace();
}
2、 生成客户端连接通告。
try {
String url = "btspp://localhost:F0E0D0C0B0A000908070605040302010;name=BTServer;authorize=false";
notifier = (StreamConnectionNotifier) Connector.open ( url );
} catch (IOException ex1) {
ex1.printStackTrace();
}
3、 获得服务记录。
serviceRecord = localDevice.getRecord(notifier);
4、 从通告获得远端蓝牙设备的连接。
streamConnection = notifier.acceptAndOpen();
dataOutputStream = streamConnection.openDataOutputStream();
dataInputStream = streamConnection.openDataInputStream();
然 后下面就是等待客户端来连接了。
七、如何创建一个蓝牙客户端
1 、搜索本地设备
try {
localDevice = LocalDevice.getLocalDevice ();
// 获得发 现代理
discoverAgent = localDevice .getDiscoveryAgent();
discoverAgent .startInquiry(DiscoveryAgent. GIAC , this );
}
catch (BluetoothStateException ex){
ex.printStackTrace();
}
// 设备发现过程中的回调(用来查找每一个可用的设备)
public void deviceDiscovered (RemoteDevice remoteDevice,DeviceClass deviceClass){
if ( device .indexOf(remoteDevice) == -1){
device .addElement(remoteDevice);
}
}
// 设备发 现完成回调(用来标识设备查找是否完成)
public void inquiryCompleted( int _int){
synchronized ( this ){
notify();
}
}
2 、 搜索远端设备提供的服务
uuid = new UUID[2];
uuid [0] = new UUID(0x1101);
uuid [1] = new UUID( "F0E0D0C0B0A000908070605040302010" , false );
for ( int i = 0; i< device .size(); i++){ // 取出已经搜到的远端设备
remoteDevice = (RemoteDevice) device .elementAt(i);
try { // 搜索该设备上的服务
discoverAgent .searchServices( null , uuid , remoteDevice , this );
} catch (BluetoothStateException ex){
ex.printStackTrace();
}
}
// 服务发现回调(用来发现远端设备上的可用服务)
public void servicesDiscovered ( int _int, ServiceRecord[] serviceRecordArray){
for ( int i = 0; i < serviceRecordArray. length ; i++){
service .addElement(serviceRecordArray[i]);
serviceRecord = serviceRecordArray[i];
}
}
// 服务发现回调(用来标识服务搜索是否完成)
public void serviceSearchCompleted( int _int, int _int1){
synchronized ( this ){
notify();
}
}
3 、与远端设备建立连接
String url = serviceRecord .getConnectionURL(ServiceRecord. NOAUTHENTICATE_NOENCRYPT , false );
try {
streamConnection = (StreamConnection)Connector.open ( url );
dataOutputStream = streamConnection .openDataOutputStream();
dataInputStream = streamConnection .openDataInputStream();
} catch (IOException ex1) {
ex1.printStackTrace();
}
经过以上步骤,服务器端和客户端就建立起了连接,相互之间就可以互相发送数据了