使用 java 蓝牙无线通讯技术...

40
【移动开发小亮点】 JABWT 【移动开发小亮点】是一个面向移动开发的社群。 希望你能在【移动开发小亮点】上进行交流学习、合作开发和研究探索。 荣誉 团队 激励 使用 Java 蓝牙无线通讯技术 API 【移动开发小亮点】 QQ 群号:16028508【移动开发小亮点】参加“Motorola SUN移动应用新亮点挑战赛”蓝牙联网开发内部资料 2006 3

Upload: others

Post on 28-Dec-2019

15 views

Category:

Documents


0 download

TRANSCRIPT

【移动开发小亮点】 JABWT

【移动开发小亮点】是一个面向移动开发的社群。

希望你能在【移动开发小亮点】上进行交流学习、合作开发和研究探索。

荣誉 ● 团队 ● 激励

使用 Java 蓝牙无线通讯技术 API

【移动开发小亮点】

(QQ 群号:16028508)

【移动开发小亮点】参加“Motorola SUN移动应用新亮点挑战赛”蓝牙联网开发内部资料

2006 年 3 月

【移动开发小亮点】 JABWT

目录 词汇翻译解释:.......................................................................................................................3

使用Java蓝牙无线通讯技术API(第一部分 -API概览) ....................4 背景...........................................................................................................................................4 典型的蓝牙应用程序实例.......................................................................................................5 蓝牙应用程序中的元素...........................................................................................................7 Java蓝牙API核心概述............................................................................................................8

蓝牙发现API ....................................................................................................................8 设备发现API ............................................................................................................9 服务发现API ............................................................................................................9 UUID类 ...................................................................................................................10 SDDB和ServiceRecord接口 ...............................................................................11 数据元素类.............................................................................................................12

设备管理API ..................................................................................................................12 LocalDevice类 .......................................................................................................12 RemoteDevice类 ..................................................................................................13 DeviceClass类 ......................................................................................................14

蓝牙通信API ..................................................................................................................14 服务器连接和客户端连接.....................................................................................16

异常.........................................................................................................................................16 蓝牙安全.................................................................................................................................16 蓝牙控制中心.........................................................................................................................17 在Sun Wireless Toolkit中对蓝牙的支持 .............................................................................17 概要.........................................................................................................................................19

使用Java蓝牙无线通讯技术API(第二部分-核心API的使用) .......20 介绍.........................................................................................................................................20 初始化蓝牙应用程序.............................................................................................................21 处理连接.................................................................................................................................23 设置一个蓝牙服务器.............................................................................................................25

创建一个服务记录.........................................................................................................26 注册服务和等候引入连接.............................................................................................26 更新服务记录.................................................................................................................27 关闭连接和移除服务记录.............................................................................................28

发现周围设备和服务.............................................................................................................28 连接到一个服务.....................................................................................................................35 锁定一个蓝牙连接.................................................................................................................37

确认一个远程设备:.....................................................................................................37 授权一个远程设备访问一个被提供的服务: .............................................................38 加密一个连接.................................................................................................................38

JABWT 和 MIDP 2.0 的安全性.........................................................................................38 JABWT和MIDP2.0 的PushRegistry ..................................................................................39

【移动开发小亮点】 JABWT

概要.........................................................................................................................................39 致谢.........................................................................................................................................40 资源.........................................................................................................................................40 关于作者.................................................................................................................................40

词汇翻译解释: cached devices ―― 贮藏设备(先前已发现的设备) discoverable ―― 可被发现的 discovery ―― 发现 given services ―― 已提供的服务 helper method ―― 助理方法 kicking off ―― 启动 makes services ―― 提供服务 notifier ―― 通告 retrieve ―― 回收 search cycle ―― 搜索循环 Securing Connection ―― 锁定连接 services of interest ―― 感兴趣的服务 Vector ―― 向量 编译匆忙,难免出错,如您发现任何纰漏和错误的话,麻烦请联系:[email protected]

【移动开发小亮点】 JABWT

使用 Java 蓝牙无线通讯技术 API(第一部分 -API 概览)

作者 C. Enrique Ortiz, 12, 2004

蓝牙是一种低成本、短距离的无线通信技术。对于那些希望创建个人局域网(PANs)的人们来说,蓝牙技术已经越来越流行了。每个个人局域网都在独立设备的周围被动态地创

建,并且为蜂窝式电话和 PDA 等设备提供了自动连接和即时共享数据的能力。

为了在 Java 平台上开发支持蓝牙技术的软件,JCP 定义了 JSR82 标准--Java 蓝牙无线技

术 APIs(JABWT)。 在这篇文章中,我将介绍一些关于蓝牙技术的背景,概述一下支持蓝牙技术的 MIDlet

应用程序的典型要素,然后介绍给你核心的 Java 蓝牙 APIs。最后我们展示一些代码来演示

如何使用这些 APIs。 实际上JSR82 定义了两个独立的可选包:核心蓝牙API和对象交换(OBEX)API。这篇

文章将对这两个中更为普遍的部分--核心蓝牙包javax.bluetooth进行详细地阐述,而OBEX API(对象交换),我们留到以后去讨论。

背景 蓝牙技术由蓝牙兴趣小组发展而来,其包括: .蓝牙无线电技术 .蓝牙协议栈 .蓝牙互操作性 profiles 蓝牙无线电技术基于在工业、科学以及医学(ISM)上公用的 2.45GHz 开放频段,这一

频段无需授权并全球通用。当蓝牙设备互相连接时,他们将组成一个微微网(piconet),即

以一个主设备和最大 7 个从设备的形式动态创建网络。蓝牙也支持 piconet 网之间的连接:

当一个 piconet 中的主设备成为另一个 piconet 的从设备时,piconet 与 piconet 间将形成桥接。 蓝牙协议栈提供了一组的高层协议和 API 以完成发现服务和模拟串行 I/O,还有一个关于

包分割和重组的低层协议以及多路技术协议和质量服务。 蓝牙互操作性 profiles--不要与 J2ME profiles 搞混--它是用来描述跨平台互操作性和一致

性需求的。蓝牙互操作性 profiles 包括三方面内容: 通用访问 profile(GAP)定义了设备管理功能性; 服务发现应用 profiles(SDP)定义了服务发现方面的内容; 串口 profiles(RFCOMM)定义了互操作设备和模拟串口电缆的能力。 你可以通过蓝牙规范(Bluetooth specification)学习这些和其它的 profiles。

【移动开发小亮点】 JABWT

蓝牙栈包含一个软件栈来映射一个固件栈(firmware),由图 1 所示:

图片 1:蓝牙协议栈 JSR82 揭示了蓝牙软件栈给Java平台的开发者。其中引起我们兴趣的是服务发现协议

(SDP),用来模拟串口的串口profile RFCOMM, 向上层协议提供诸如分割和重组等导向

性连接的数据转换操作的逻辑链路控制及适配profile(L2CAP),以及多路技术协议。注意:

JABWT不支持无连接L2CAP。 JABWT 也包括对象交换 API。OBEX 也是高层 API,它用来交换对象数据,诸如电子

商业卡和日历标签之间以 vCard 和 vCalendar 的格式进行数据传输。在蓝牙上,对象交换通

过 RFCOMM 发生。OBEX 在最开始时是由红外(IrDA)引入的,并且它可以在 IrDA 协议、

TCP/IP 或者是其他协议的顶层实现。

典型的蓝牙应用程序实例 一个开启蓝牙功能的应用可以作为一个服务端或是一个客户端-- 一个服务的提供者或是

消费者,或者它可以作为一个真正的点对点终端同时表现出服务和客户的行为。图 2 所示一

个蓝牙规范用例:

【移动开发小亮点】 JABWT

图 2:一个典型的具有蓝牙功能的实际用例

对这些用例的简要介绍:

.初始化--所有具备蓝牙功能的应用程序必须先要初始化蓝牙栈。 .客户端-- 一个客户对远端服务进行消费。首先它要发现所有附近的设备,然后对

于每一个发现的设备搜索它感兴趣的服务。 .服务器端-- 一个为客户端提供服务的服务器。它在服务发现数据库(SDDB)中

对客户端进行注册,对他们进行有效广播。然后等待引入的连接,在他们进入时接受他们并

为他们提供服务。最后,当不再需要服务时,应用程序会在服务发现数据库(SDDB)中将

他们移除。 图三:用例中参与活动的图表:

【移动开发小亮点】 JABWT

图 3: 蓝牙应用程序活动图

蓝牙应用程序中的元素 图 4 显示了在 MIDlet 中一个典型蓝牙功能应用程序中的一些元素:

图 4: 具有蓝牙功能的 MIDlet (高端组织)

中间的是核心应用程序 My Bluetooth MIDlet,它扩展自 javax.microedition.midlet.MIDlet。

没有显示出来的还有 MIDlet 中实现的 javax.microedition.lcdui.CommandListener 以监听从用

户接口中输入的命令。应用程序使用的剩余的类和接口都包含在了蓝牙规范中,像设备发现

和服务,连接和服务消费,还有广播和提供服务。

【移动开发小亮点】 JABWT

使用诸如 MVC 等设计模式是很好的实践。MVC 把应用程序分解成用户接口(视图),

应用程序行为和导航(控制器),以及数据(模型),当然在我们的案例中还要加上蓝牙 API的支撑类和接口。像将分离的客户端、服务端行为构建成独立的类以便以后可以重用这些组

件,也是很好的设计。

Java 蓝牙 API 核心概述

JSR82 需求的"最小公分母"是 受限连接设备配置(CLDC),可靠连接设备配置(CDC)是 CLDC 的超集,所以 JABWT 可以同时在 CLDC 和 CDC 上实现,简要地说,你可以在使

用任何 J2ME profile 的上使用 JABWT。 在javax.bluetooth中我们可以发现,Java蓝牙API可以被分解为三个部分,在下面我们将讨

论到它们:蓝牙发现、设备管理和蓝牙通信。

蓝牙发现 API

客户端程序使用蓝牙发现 API 以搜索在其附近的设备和服务。服务代理类

(DiscoveryAgent)同时支持设备与服务的发现。当设备和服务被发现时,想得到通知的客

户端应用程序必须实现并注册 DiscoveryListener 接口,这个接口定义了设备发现通知和服务

发现通知的回调。 发现代理(DiscoveryAgent)与蓝牙客户端应用程序之间是典型的一对一的关系:

【移动开发小亮点】 JABWT

图 5: DiscoveryAgent 类和 DiscoveryListener 接口

设备发现 API

你使用 DiscoveryAgent 类的"设备发现"方法来开始和取消设备发现: .retrieveDevices()重新获得已经发现或者附近的已知设备 .startInquiry() 启动发现附近设备,也叫 inquiry .cancelInquiry()取消当前进行的任何请求 蓝牙发现代理在请求阶段的不同时候会分别调用 DiscoveryListener(发现监听器)不

同的回调方法: .deviceDiscovered() 指出是否有设备被发现。 .inquiryCompleted() 指出是否请求已经成功、触发一个错误或已被取消。 在图 6 中的状态图表阐明了设备发现的状态改变结束于相应的回调方法的返回。

图 6: 设备发现状态表

设备发现以调用 startInquiry()函数开始。在请求进行时,蓝牙发现代理会在适当的时候调用

回调方法 DeviceDiscovered()和 inquiryCompleted()。

服务发现 API

你可以使用发现代理的服务发现方法来开始或取消服务发现: . selectService()启动服务发现搜索。(原文有误,根据 API 手册应为尝试定位一个服务) . searchServices()启动服务发现搜索。 . cancelServiceSearch()取消在正在进行中的任何的服务发现搜索操作。

【移动开发小亮点】 JABWT

蓝牙发现代理在服务发现阶段的不同时候会分别调用 DiscoveryListener 的服务发现回调

方法: . servicesDiscovered() 表示是否服务已被发现。 . serviceSearchCompleted()表示服务发现是否已经完成。 图 7 阐明了服务发现的状态改变结束于 DiscoveryListener 的回调方法的返回。

图 7: 服务发现状态图表

服务发现开始于对 searchServices()的调用。当服务搜索进行时,蓝牙发现代理会在适当的时

候回调 servicesDiscovered()和 serviceSearchCompleted()方法。 除了 DiscoveryAgent 和 DiscoveryListener 了,你在服务发现过程中还要使用到的类有

UUID,ServiceRecord 以及 DataElement 等。

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 对象,你可以去掉连字符。

【移动开发小亮点】 JABWT

SDDB 和 ServiceRecord 接口

在服务发现的中心是服务发现数据库(SDDB)和服务发现协议(SDP)。SDDB 由蓝牙实现

负责维护的数据库。它包含了服务记录(service records),后者代表了对客户端有效的服务。

SDP 对于基于 JABWT 应用程序来说是透明的;可以这么说,SDP 是用于服务发现的。为重

新获取服务纪录,一个本地设备 SDP 客户端会向一个远端设备上 SDP 服务器发出请求。

图 8: SDDB

每一笔服务记录都会由一个 ServiceRecord 的实例来表现。这个记录包含了描述服务细节的

属性。这个类提供了几种有用的方法: .getAttributeIDs() 和 getAttributeValue()方法返回服务记录的属性。

.getConnectionURL()方法获取链接的 URL 地址给服务器主机来收集服务记录。 .getHostDevice() 方法获取提供服务的远端设备。 .populateRecord() 和 setAttributeValue()方法用来设置设备记录的属性。 .setDeviceServiceClasses()方法设置服务的类。 图 9 显示了蓝牙本地设备和远端设备,以及 SDDB 还有服务记录之间的关系:

图 9: 使用远端设备,SDDB 和服务记录进行服务发现

为使服务端可以被客户端来使用,服务应用程序要通过如下方法建立一个服务记录,首

先要创建一个连接通知器(connection notifier),然后由调用连接通知器的 acceptAndWait()方法来向 SDDB 中插入记录。服务端程序能够在适当的时候获得记录和更新。客户端应用

程序向远端 SDDB 请求可以使用的服务,会发现服务记录。

【移动开发小亮点】 JABWT

数据元素类

一个服务可以有许多的属性,一些是强制性的,其他的是可选的。一个服务属性由一个

数据元素对象来表现,这个数据元素对象提供了设置并取得属性值的方法。 强制性属性是在注册一个服务之后被自动设定的。这些属性包括:ServiceRecordHandle,

ServiceClassIDList, ServiceRecordState, ServiceID, 还有 ProtocolDescriptorList。 如果你想要的话,还可以设置可选属性。可选属性有很多,但是有三个值得关注:

ServiceName,ServiceDescription, 和 ProviderName。 想得到更多的关于这些属性的信息,请参看 JABWT 的文档或蓝牙规范。

设备管理 API

有 3 个主要的类来支持设备管理: .LocalDevice .RemoteDevice .DeviceClass

LocalDevice 类

LocalDevice 类标识了本地蓝牙设备。蓝牙应用程序和 LocalDevice 之间的关系是典型的一对

一关系:

图 10:本地设备类

本地设备提供了方法来返回关于本地设备的信息,并且能够进入 Bluetooth manager:

.getBluetoothAddress()返回蓝牙设备地址。

.getDeviceClass()返回设备类。

【移动开发小亮点】 JABWT

.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 文档中或是规范中学习更多的有关蓝牙属性的内容。

RemoteDevice 类

一个 RemoteDevice 的实例代表了一个远端蓝牙设备。在一个蓝牙客户端应用程序可以进

行服务,消费之前,它必须发送一个设备请求来发现远端设备。典型的蓝牙应用程序和远端

设备之间的关系是一对多:

图 11: 远端设备类

远端设备(RemoteDevice)提供的方法中,有些很类似于本地设备(LocalDevice)里提供的

方法: .getBluetoothAddress()返回蓝牙地址。 .getFriendlyName()返回蓝牙设备名。

【移动开发小亮点】 JABWT

.getRemoteDevice()返回相应的被指定蓝牙连接的远端设备。

.authenticate()尝试识别验证远端设备。

.authorize()为指定的蓝牙连接去尝试批准远端设备访问本地设备。

.encrypt()尝试为指定的蓝牙连接开启或关闭加密。

.isAuthenticated()测试是否远端设备可以被验证。

.isAuthorized()测试是否远端设备已经被蓝牙控制中心授权访问本地设备以进行蓝牙连接。

.isEncrypted()测试是否本地设备和远端设备之间的通信被加密。

.isTrustedDevice()测试是否远端设备被蓝牙控制中心指定为可信任的。

DeviceClass 类

一个 DeviceClass 对象代表一个设备的设备类(CoD),例如一个打印机或者一部电话。

CoD 包括一个主类,一个辅的类,和服务类型或服务类。DeviceClass 提供了如下方法: .getMajorDeviceClass()方法获取设备的主类。 .getMinorDeviceClass()方法获取设备的辅类。 .getServiceClasses()获取设备的服务类。 当一个设备被发现,同时他的类也会被发现;当发现代理调用 deviceDiscovered()时,其

中一个参数就是 DeviceClass。你可以通过它的 getDeviceClass()方法找到本地设备的 CoD。

蓝牙通信 API

JABWT 连接是基于逻辑链路及适配层协议的。L2CAP 是一个低级协议用来管理数据

包,直到达到 64k。L2CAP 中的处理细节像消息分割和重组(SAR),多路连接。另外,串

口 Profile(SPP)提供 RFCOMM,一个通过 L2CAP 层的串行模拟协议。 L2CAP 和 RFCOMM 连接都是基于通用连接框架(GCF)的,直接通向接口和类的层次

去创建连接和执行 IO 命令。JABWT 依靠 L2CAP 和 RFCOMM 协议扩展了通用连接框架

(GCF)以分别支持通过 L2CAPConnection 和 StreamConnection 类型连接。就在

L2CAPConnection 被 JSR 82 介绍的同时,StreamConnection 在原始的 javax.microedition.io的 GCF 中被定义了,它是依靠 CLDC 发展来的。注意,JABWT 中 L2CAPConnection 仅支

持面向连接的 L2CAP 连接。图 12 显示了以基于 GCF 形式的各接口通过蓝牙网络进行通信

的过程:

【移动开发小亮点】 JABWT

图 12: 通用连接框架和蓝牙连接类型

层次定义了 L2CAP 和 Stream 的连接和连接通知器。一个连接定义了一个连接终端,当

一个连接通知器执行了服务行为时,它会等待并接受 L2CAP 连接处理。 处理 L2CAP 连接比处理流连接更复杂。使用 L2CAP 时,开发者们必须处理好最大消息

的大小(即最大传输单位,或叫做 MTU)、打断和重组等。这些复杂的事项在开发者使用流

连接的时候被隐藏了,以使他们处理蓝牙连通时获得更好的效率。 如 同 所 有 的 GCF 连 接 类 型 , 你 可 以 通 过 调 用 GCF 连 接 工 厂 方 法

javax.microedition.io.Connector 创建蓝牙连接。传递给 Connector()方法的连接 URL 决定了要

创建的连接类型: 一个 L2CAPConnection 连接的 URL 格式:

btspp://hostname:[CN | UUID];parameters

一个 RFCOMMStreamConnection 连接的 URL 格式:

btspp://hostname:[CN | UUID];parameters

细节介绍: .btl2cap 是为 L2CAPConnection 设计的 URL 配置方案。 .btspp 是为 RFCOMM StreamConnection 的 URL 配置方案。 .hostname 既可是 localhost 用于架设一个服务器连接,也可是一个用于创建客户端连接的蓝

牙地址。 .PSM 是协议/服务多路复用值,在一个客户端连接服务器端时使用。在概念上是模拟一个

TCP/IP 端口。 .CN 是信道数值,在一个客户端连接服务器端时使用,也是模拟 TCP/IP 端口。 .UUID 是 UUID(通用唯一标识符)值,在一个服务器上建立服务时使用。

【移动开发小亮点】 JABWT

.parameters(参数)包括描述服务名称的名字和有于安全的参数:验证、授权和加密。

服务器连接和客户端连接

在连接的 URL 中,主机名称告诉连接工厂是否它应该创建一个客户端异或服务器端。如

果使用单词 localhost 作为主机名将定义一个服务器连接。客户端想要连接到一个指定的的

服务可以通过调用 ServiceRecord.getConnectionURL()来找到该服务连接的 URL。

异常 javax.bluetooth 核心 API 定义了三个异常类: BluetoothConnectionException:当一个蓝牙 L2CAP、RFCOMM 或是 OBEX-over-RFCOMM连接不能被成功建立会抛出。 BluetoothStateException:一个试图在错误状态下进行蓝牙操作时会抛出。 ServiceRegistrationException:异常.当在本地服务发现数据库(SDDB)中,添加或改变服务

记录失败时会抛出。

蓝牙安全 一个安全的蓝牙连接应该是经过验证、可选的获得授权和被加密过的。这样,蓝牙连接

在建立之初或以后就可以保证其安全性了。

注意:不是所有的蓝牙实现都提供了安全连接的。 为了在建立一个蓝牙连接时使其安全,就要给 javax.microedition.io.Connector 在连接 URL 字

符串上提供适当的安全参数:

btspp://hostname:[CN |

UUID];authenticate=true;authorize=true;encrypt=true

其中: .authenticate 验证一个连接设备的身份。 .authorize 授权一个连接中的设备(已被识别)是否被允许进入。 .encrypt 指定连接需被加密。 你已经看到了想要连接到一个服务的客户端可以通过调用

ServiceRecord.getConnectionURL()方法以获得服务连接的 URL。该方法中的一个参数

requiredSecurity,指定了返回的这个连接 URL 是否应该包含可选的 authenticate 和 encrypt等安全参数。关于 requiredSecurity 的有效值为:

.ServiceRecord.NOAUTHENTICATE_NOENCRYPT 意为 authenticate=false; encrypt=false。

.ServiceRecord.AUTHENTICATE_NOENCRYPT 意为 authenticate=true; encrypt=false。

.ServiceRecord.AUTHENTICATE_ENCRYPT 意为 authenticate=true; encrypt=true。 例如:

【移动开发小亮点】 JABWT

...

ServiceRecord sr = ...;

...

String connURL =

sr.getConnectionURL(ServiceRecord.AUTHENTICATE_ENCRYPT, false);

...

如果不在建立一个连接的时候就使用这种方法保证安全性,你也可以在以后确保其安全

性,这就要使用一组已知的远端设备的安全方法:authenticate(),authorize()和 encrypt()。如

果你这样做了请注意,验证必须在授权和加密之前进行。

蓝牙控制中心 蓝牙控制中心(BCC)是设备上的一个管理软件,它负责在作为授权中心改变本地蓝牙

的设置,包括:打开或关闭蓝牙无线电,设置友好名称以便在设备发现过程中进行广播,开

启或屏蔽设备的发现模式,设置 PIN 码,设置缺省安全属性等等。BCC 看上去表现如何全

看实现的具体情况。

在 Sun Wireless Toolkit 中对蓝牙的支持 J2ME Wireless Toolkit 2.2 支持 JABWT。toolkit 的 Preferences Utility(使用偏好)提供了

新的 Bluetooth/OBEX 标签以进行蓝牙参数设置。在此栏下 OBEX 设置和三个蓝牙子标签,

它们分别是:Internal Properties(内部属性)标签允许你设置设备搜索的超时时间,System Properties(系统属性)标签允许你定义一些调用 LocalDevice.getProperty()方法便可得到蓝牙

属性。BCC 属性(BCC Properties)标签允许你去设置设备友善的名字,可被发现的模式, 和安全特性. 图 13 将展示给你副标签的三个分隔屏幕:

【移动开发小亮点】 JABWT

【移动开发小亮点】 JABWT

图 13: 设置蓝牙参数选择

概要 本文的第一部分介绍了 JABWT(Java APIs for Bluetooth Wireless Technology)。对于允许个

人设备共享数据和服务的个人局域网来说,蓝牙是一个激动人心的无线技术。本文已涵盖了

大部分的领域,包括一些关于蓝牙背景信息,一个典型的基于蓝牙 MIDlet 的基本原理的概

述,,以及一些关于核心 Java 蓝牙 API 的介绍。在本文的第二部分将涵盖如何使用这些核心

JABWT 接口。

【移动开发小亮点】 JABWT

使用 Java 蓝牙无线通讯技术 API(第二部分-核心 API 的使用)

作者C. Enrique Ortiz, 02, 2005

蓝牙是一种低成本、短距离的无线通信技术。对于那些希望创建个人局域网(PANs)的人们来说,蓝牙技术已经越来越流行了。每个个人局域网都在独立设备的周围被动态地创

建,并且为蜂窝式电话和 PDA 等设备提供了自动连接和即时共享数据的能力。 为了在 Java 平台上开发支持蓝牙技术的软件,JCP 定义了 JSR82 标准--Java 蓝牙无线技

术 APIs(JABWT)。 在第一部分(API概览)中,已经概述了蓝牙技术和JABWT(Java APIs for Bluetooth Wireless

Technology)以及一些使用实例、运用领域和蓝牙应用的典型要素,同时也介绍了定义于

javax.bluetooth 包中的核心API。在第二部分中将注重解释蓝牙无线技术API是如何使用

的。通过展示一些代码来演示如何使用这些核心API来初始化应用程序,处理连接,创建服

务,发现临近周围的设备,连接服务以及设置连接安全保障。

介绍 让我们先简短地回顾一下典型的使用实例和基于蓝牙应用的领域。一个蓝牙应用可以做

为服务器端或者是一个客户端―― 一个服务的提供者或一个服务的消费者,或者也可作为

一个对等网络终端来运行,同时表现为服务器和客户端的性状。典型的蓝牙应用有三种使用

实例:

图 1: 蓝牙使用实例

初始化――任何一个基于蓝牙应用,服务器或客户端,必须首先初始化蓝牙栈。

【移动开发小亮点】 JABWT

客户端――一个客户端消费远程服务,它首先得发现四周的一切设备,然后再在它所发现的

每个设备里搜索它感兴趣的服务。 服务器――一个服务器提供可使用的服务给客户端。它得先注册所提供的服务到服务发现数

据库里,有效地广播他们。然后等着引入连接,当它们进入时接受它们,再提供服务给客户

端。最后不再需要服务时,应用程序将把它从服务发现数据库(SDDB)中移除。 图 2 已经图示了典型的蓝牙客户端和服务器的应用:

图 2: 服务器和客户端的活动

你可看到在图 2 里,客户端和服务器端执行初始化,服务器准备了一个服务并等候连接,而

客户端发现了设备和服务,然后连接特定的设备并消费独特的服务。 现在让咱们训练一些可以展示如何使用蓝牙的 java API 来实现这些活动的示例代码。

初始化蓝牙应用程序 正如你所看到的,图 3 中 ,初始化是一个很简单的操作。

【移动开发小亮点】 JABWT

图 3: 初始化蓝牙应用程序 首先应用程序要从本地设备中回收一个相关的蓝牙管理器,而客户端则要回收一个相关的发

现代理,它可提供所有发现相关的服务。服务应用程序设立可被发现的设备,下面的代码片

断,是初始化方法 btInit()执行客户端和服务器的初始化:

... private LocalDevice localDevice; // 本地蓝牙管理器 private DiscoveryAgent discoveryAgent; // 发现代理 ... /** * 初始化 */ public void btInit() throws BluetoothStateException { localDevice = null; discoveryAgent = null; //回收本地设备获得蓝牙管理器 localDevice = LocalDevice.getLocalDevice(); // 服务器设置被发现的模式为 GIAC (通用接口访问控制) localDevice.setDiscoverable(DiscoveryAgent.GIAC); // 客户端回收发现代理 discoveryAgent = localDevice.getDiscoveryAgent();} ...

并非所有的应用程序都同时作为服务器和客户端的,它们的角色分配应该根据你的应用需求

来安排。服务器应用程序设置自身为可被发现的,而客户端应用程序则获得相关的发现代理

来发现服务。当你可以通过调用LocalDevice.setDiscoverable() 方法来设置设备为可被发

现的模式,必须指定查询访问码(IAC)。JABWT支持两种访问模式: DiscoveryAgent.LIAC 指定有限查询访问码。设备将在唯一的有限时间周期(典型为 1 分钟)

里可被发现。经过有限的周期后,设备会自动地返回到无法发现的模式。 DiscoveryAgent.GIAC 指定通用查询访问码。没有限制地被设置为可被发现模式在设备中长

期驻存。

【移动开发小亮点】 JABWT

为了强制一个设备回到无法发现模式,简单地调用 LocalDevice.setDiscoverable()方法用

DiscoveryAgent.NOT_DISCOVERABLE 作参数。 注意:在景物背后,蓝牙控制中心(BBC)服务器作为中枢管理着本机所有的蓝牙设置。

BCC 使用户能定义和清除可被发现模式和其他的设置。同时 BCC 也能保护所有的应用程序

免于其他应用程序的不利影响。想了解更多,请你自己去查看规范。 无论何时一个设备设为可被发现的,对于其他蓝牙设备来说它就是可见的,因而启动后易受

攻击。尽管通用查询访问码(GIAC)是最普遍用的查询访问码,为了减少风险你应考虑使

用有限查询访问码来设置设备仅在需要的时候可被发现,可能只是很短的一段时间。BCC允许你禁用可被发现模式。

处理连接 在第一部分中你已知道 JABWT 连接基于逻辑连接链路控制适配层协议(L2CAP),一个低

层数据包协议,在 L2CAP 之上的一个串行模拟器协议被串口 profile(SPP)RFCOMM 支

持。JABWT 连接是基于通用连接框架(GCF)和由 L2CAPConnection 和 StreamConnection 类型分别代表。第一部分中也提到,当 L2CAPConnection 被用 JSR82 介绍时, StreamConnection 被定义为原 javax.microedition.io GCF 中在连接 CLDC 时的一部分。 正如所用的 GCF 连接类型,你创建一个蓝牙连接时使用 GCF 连接的工厂模式

javax.microedition.io.Connector,,通过它的 open()方法一个 URL 连接参数描述创建的连接终

端。连接 URL 方案决定了创建的连接类型: 一个 L2CAPConnection 的 URL 格式: btl2cap://hostname:[PSM | UUID];parameters 一个 RFCOMMStreamConnection 的 URL 格式: btspp://hostname:[CN | UUID];parameters 其中: btl2cap――是一个 L2CAPConnection 的连接 URL 方案。 Btspp――是一个 RFCOMMStreamConnection 的连接 URL 方案。 Hostname――可以是本地主机建立一个服务器连接,也可以是创建一个客户端连接的蓝牙

地址。 PSM――是协议/服务多路复用值,被一个客户端用来连接到一个服务器。在概念上类似于

一个 TCP/IP 的端口。 CN――是通道编号值,被一个客户端用来连接到一个服务器――也在概念上类似于一个

TCP/IP 的端口。 UUID――是全球唯一标识符被一个服务器用来设置一个服务。每一个 UUID 必须保证所有

的时间和空间上都是唯一的。

【移动开发小亮点】 JABWT

参数包括:name――描述服务名,和安全参数:authenticate,authorize 和 encrypt。 举例: 一个服务 RFCOMM URL btspp://localhost:2D26618601FB47C28D9F10B8EC891363;authenticate=false;

encrypt=false;name=MyBtService

一个客户端 RFCOMM URL btspp://0123456789AF:1;master=false;encrypt=false;authenticate=false

使用本地主机做主机名说明你想要一个服务连接。创建一个客户端连接已知的设备和服务,

使用服务连接 URL,寻找它的服务号。 处理 L2CAP 连接更多是处理流连接;开发者必须处理最大信息长度(maximum transmission unit, or MTU),处理断路和重新装配长信息。这些复杂性都被隐藏了通过开发者使用流连接,

使它们优先于蓝牙的连通。代码的样例在本文中只涵盖了流连接。 以下的代码片断将展示如何去创建一个 RFCOMM 服务连接:

... //蓝牙服务名 private static final String myServiceName = "MyBtService"; //蓝牙服务的兴趣 UUID private static final String myServiceUUID = "2d26618601fb47c28d9f10b8ec891363"; private UUID MYSERVICEUUID_UUID = new UUID(myServiceUUID, false); ... // 定义服务连接 URL String connURL = "btspp://localhost:"+MYSERVICEUUID_UUID.toString()+";"+name="+myServiceName; ... // 创建一个服务连接(一个通告) (a notifier) StreamConnectionNotifier scn = (StreamConnectionNotifier) Connector.open(connURL);... // 接受一个新的客户端连接 StreamConnection sc = scn.acceptAndOpen();...

以下的代码片断将展示如何去创建一个客户端连接到已提供的感兴趣的服务,使用它的服务

记录:

... // 已提供一个感兴趣的服务, 获得它的服务记录 ServiceRecord sr = (ServiceRecord)discoveredServices.elementAt(i);

【移动开发小亮点】 JABWT

// 然后得到服务器的连接 URL String connURL = sr.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false); // 打开连接 StreamConnection sc = (StreamConnection) Connector.open(connURL); ...

如果你正在MIDP2.0 手机上开发你的JABWT应用程序,希望连接前它必须请求正当的准许,

否则Connector.open()将抛出一个SecurityExecption例外。稍后我将告诉你如何去请求许

可,在本文中的“JABWT 和 MIDP 2.0 的安全性”部分。

设置一个蓝牙服务器

图 4: 设置一个蓝牙服务器

你要设置一个能提供消费服务的蓝牙服务器,将有四个主要步骤: 1.创建一个你想要提供的可消费服务的服务记录, 2.增加一个新的服务记录到服务发现数据库, 3.注册服务。 4.等候客户端的引入连接。 两个有重要意义的相关操作: 1.修改服务记录,如果服务属性能使客户端可视需要改变; 2.当所有的都做了,从 SDDB 移除服务记录。 让我们注视这些操作,

【移动开发小亮点】 JABWT

创建一个服务记录

蓝牙的实现会自动地创建一个服务记录当你的应用程序创建一个连接通告时,可能是一个

StreamConnectionNotifier 通告或者是一个 L2CAPConnectionNotifier 通告, 每个蓝牙服务和服务属性都是自身的全球唯一识别符,你不能创建一个服务而没有起初分配

给它的一个 UUID,足够简单:UUID 类表示短的(16-或 32-位)和长的(128-位)UUID。

生成一个 UUID,如果你是运行在 Linux 操作系统下的话,使用命令 uuidgen-t;或在 Windows系统下使用命令 uuidgen。Uuidgen 的效用包括连字符在生成 UUID;移除这些连字符当你拷

贝输出引入你的源代码的时候。 在本文中获得一个UUID的使用范例,我运行uuidgen-t而生成了2d266186-01fb-47c2-8d9f-

10b8ec891363.。以下的代码片断定义了服务中一些私有的成员,例如,服务名和服务UUID成员:

... // 蓝牙服务名 private static final String myServiceName = "MyBtService"; // 蓝牙服务 UUID private static final String myServiceUUID = "2d26618601fb47c28d9f10b8ec891363"; private UUID MYSERVICEUUID_UUID = new UUID(myServiceUUID, false); private UUID[] uuids = {MYSERVICEUUID_UUID}; ...

下一个片断定义和示例了一个 RFCOMM 连接通告,引发服务记录的创建。

... StreamConnectionNotifier streamConnectionNotifier; // 创建通告 (和服务记录) streamConnectionNotifier = (StreamConnectionNotifier) Connector.open(connectionURL); ...

注册服务和等候引入连接

一旦你创建了连接通告和服务记录,服务器就准备注册服务和等候客户端。调用通告的

【移动开发小亮点】 JABWT

acceptAndOpen()方法可使蓝牙实现插入服务记录联结到 SDDB,提供可发现的服务给客户

端。AcceptAndOpen()方法中断后,等候引入允许进入的连接:

... // 插入服务记录到 SDDB 和等候一个引入连接的客户端 StreamConnection conn = streamConnectionNotifier.acceptAndOpen(); ...

当一个客户端连接时, acceptAndOpen()返回一个连接,在我们的例子中的一个

StreamConnection 提供服务器将读取数据的客户终端。以下的代码片断是等待和接受一个

引入客户端连接以及读取数据。

... // 等候一个客户端连接 StreamConnection conn = streamConnectionNotifier.acceptAndOpen(); //新客户端的连接接受,获得一个操作。 RemoteDevice rd = RemoteDevice.getRemoteDevice(conn); System.out.println("New client connection... " + rd.getFriendlyName(false)); // 读取输入信息, 从这个样例的字符串中。 DataInputStream dataIn = conn.openDataInputStream(); String s = dataIn.readUTF(); //传送接受报文到输入信息监听器 ...

这段代码段从连接中读取了一个样例字符串 String。当然,不同的应用程序有不同的数据编

码需要。一个字符串 String 已足够一个聊天应用程序,而一个多媒体应用程序将可能使用字

符和二进制数据混用组合。 注意这段代码模块在等候引入客户端连接时,必须发送自身的执行线程,如果它被系统线程

调用的话,用户界面将冻结,而你的应用程序也将停顿。

更新服务记录

有一些原因促使一个已注册服务的属性必须改变时,你可以使用本地蓝牙管理器更新 SDDB中的记录。正如下面的代码片断所展示的,你通过调用 LocalDevice.getRecord()从 SDDB 中

回收记录,通过调用 ServiceRecord.setAttributeValue()增加或改变兴趣的属性,和通过调用

LocalDevice.updateRecord()把服务写进 SDDB:

... try {

【移动开发小亮点】 JABWT

// 回收服务记录 和设置/更新 任意的属性, // 举例, ServiceAvailability, 标志服务是可用的 sr = localDevice.getRecord(streamConnectionNotifier); sr.setAttributeValue(SDP_SERVICEAVAILABILITY, new DataElement(DataElement.U_INT_1, 0xFF)); localDevice.updateRecord(sr); } catch (IOException ioe) { // 捕捉例外, 显示错误 } ...

关闭连接和移除服务记录

当服务不再需要时,通过关闭连接通告把它从 SDDB 中移除

... streamConnectionNotifier.close(); ...

发现周围设备和服务

图 5: 发现设备和服务

一个客户端在找到服务前是不可以消费服务的。搜索循环由已发现的周围设备,和发现了的

【移动开发小亮点】 JABWT

设备上搜索感兴趣的服务所构成。发现设备的请求是昂贵和耗时的。客户端常常通过找出是

否已知或贮藏设备已经提供过感兴趣的服务来避免这个开销,初始化一个新的请求只在它们

还没有提供过服务的时候。 发现功能是DiscoveryAgent的职责所在,这个类允许客户端初始化和取消设备和服务的发现

功能,DiscoveryAgent通过DiscoveryListener 接口通告客户端的应用程序所发现的设备和服

务。图 6 图解了蓝牙客户端,DiscoveryAgent和DiscoveryListener之间的关系。

图 6: The发现代理和发现监听器

客户端调用 DiscoveryAgent的 retrieveDevices() 方法来回收已发现或贮藏的设备:

RemoteDevice[] retrieveDevices(int option); 有如下的选项: CACHED 这个方法是否将返回先前已发现的设备 PREKNOWN 它是否将返回已知设备 客户端初始化一个搜索循环通过调用startInquiry(): boolean startInquiry(int accessCode, DiscoveryListener listener); ...其中 accessCode是DiscoveryAgent.LIAC或者 DiscoveryAgent.GIAC,正如你之前看到的一样 listener是搜索监听器 从DiscoveryAgent回收发现通告客户端应用程序必须实现DiscoveryListener接口和它的四个

发现回叫: deviceDiscovered(),inquiryCompleted(),servicesDiscovered(),serviceSearchCompleted(),如图所示在这个示例的类中:

public class BtClient implements DiscoveryListener { ... Vector discoveredDevices = new Vector(); ... // DiscoveryListener 回叫

【移动开发小亮点】 JABWT

/** * deviceDiscovered() 被 DiscoveryAgent 调用当 *它发现 一个设备在一个请求里 */ public void deviceDiscovered( javax.bluetooth.RemoteDevice remoteDevice, javax.bluetooth.DeviceClass deviceClass) { // 追踪已发现的远程设备 通过把它们插入 // 到向量中 ... } /** * inquiryCompleted() 被 DiscoveryAgent 调用当 * 一个设备搜索循环完成 */ public void inquiryCompleted(int param) { // 现在请求已经完成, 如果已被发现的任何 // 一个设备的触发器 搜索服务 ... } /** * servicesDiscovered() 被 DiscoveryAgent 调用当 * 一个服务搜索找到服务时,. * transID 识别服务搜索返回的结果. * 服务记录 保存相关的已被找到的服务 */ public void servicesDiscovered(int transID, javax.bluetooth.ServiceRecord[] serviceRecord) { //追逐已发现的服务 加它们到 // 向量中 ... } /** * serviceSearchCompleted() 被 DiscoveryAgent 调用 * 实现当一个服务搜索完成 * transID 识别一个 独特的服务搜索 * responseCode 指示 为什么服务搜索结束了. */ public void serviceSearchCompleted (int transID, int responseCode) {

【移动开发小亮点】 JABWT

//现在服务搜索已经完成, // 发送线程来处理已发现的服务 ... } ... }

助理方法 btInitiateDeviceSearch(),可被调用来响应一个客户的请求,回收一切的贮藏或先

前发现的设备,然后启动一个请求:

/** * btInitiateDeviceSearch()启动设备的发现功能 */ public void btInitiateDeviceSearch() { System.out.println("BTMIDlet.btInitiateDeviceSearch"); int i, s; ... remoteDevices.clear(); discoveredDevices.clear(); ... RemoteDevice[] cachedDevices = discoveryAgent.retrieveDevices(DiscoveryAgent.CACHED); if (cachedDevices != null) { s = cachedDevices.length; for (i=0; i<s; i++) { remoteDevices.put( cachedDevices[i].getBluetoothAddress(), cachedDevices[i]); } } ... RemoteDevice[] preknownDevices = discoveryAgent.retrieveDevices(DiscoveryAgent.PREKNOWN); if (preknownDevices != null) { s = preknownDevices.length; for (i=0; i<s; i++) { remoteDevices.put( preknownDevices[i].getBluetoothAddress(), preknownDevices[i]); } } ...

【移动开发小亮点】 JABWT

try { inquiryStarted = discoveryAgent.startInquiry(

DiscoveryAgent.GIAC, this); } catch(BluetoothStateException bse) { // 显示错误信息 System.out.println("Inquiry Failed"); return; } if (inquiryStarted == true) { // 显示进程信息 System.out.println("Inquiry in Progress"); } else { // 显示错误信息 System.out.println("Inquiry Failed"); } }

让咱们更仔细地看清楚两个收回的 DiscoveryAgent 调用,再看客户端是如何处理被发现的

设备的:

/** * deviceDiscovered() 被 DiscoveryAgent 调用当 * 它发现一个设备在查询的时候. */ public void deviceDiscovered( javax.bluetooth.RemoteDevice remoteDevice, javax.bluetooth.DeviceClass deviceClass) { System.out.println("BTMIDlet.deviceDiscovered"); // 追踪被发现的远程设备 discoveredDevices.put( remoteDevice.getBluetoothAddress(), remoteDevice); } /** * inquiryCompleted() 被 DiscoveryAgent 调用当 * 设备发现完成. * @参数 type 是已完成发现设备请求的类型 * INQUIRY_COMPLETED, INQUIRY_TERMINATED, or INQUIRY_ERROR */ public void inquiryCompleted(int type) {

【移动开发小亮点】 JABWT

System.out.println("BTMIDlet.inquiryCompleted"); int i, s; // 在每一个请求完成后, 更新屏幕 // 现在请求已经完成, 重新移动 // 被发现的设备 移到 远程设备的向量载体中 remoteDevices Vector s = discoveredDevices.size(); if (s > 0) { System.out.println(s + " device(s) found"); for (i=0; i<s; i++) { RemoteDevice rd = (RemoteDevice) discoveredDevices.elementAt(i); // 添加设备,只在未知或隐藏的时候 RemoteDevice rd2 = (RemoteDevice) remoteDevices.get(rd.getBluetoothAddress()); if (rd2 == null) { remoteDevices.put(rd.getBluetoothAddress(), rd); } } } else { System.out.println("No devices found"); } // 展示远程设备 String friendlyName; s = remoteDevices.size(); if (s > 0) { System.out.println(s + "device(s) found"); for (i=0; i<s; i++) { RemoteDevice rd = (RemoteDevice) remoteDevices.elementAt(i); try { friendlyName = rd.getFriendlyName(false); } catch (IOException ioe) { friendlyName = null; } if (friendlyName == null) { System.out.println(rd.getBluetoothAddress()); } else { System.out.println(friendlyName); } }

【移动开发小亮点】 JABWT

} }

一旦周围的设备被发现,客户端就可以搜索感兴趣的服务。以下的代码片断将展示助理方法

btInitiateServiceSearch()负责启动服务搜索:

/** * btInitiateServiceSearch() 启动服务发现 */ public void btInitiateServiceSearch() { System.out.println("BTMIDlet.btInitiateServiceSearch"); int s, i; ... discoveredServices.clear(); // 初始化在远程设备上的服务搜索 s = remoteDevices.size(); if (s==0) { System.out.println("No devices to search..."); } else { for (i=0; i<s; i++) { RemoteDevice rd = (RemoteDevice) remoteDevices.elementAt(i); try { transID = discoveryAgent.searchServices( attrs, uuids, rd, this); // 显示进程信息 System.out.println( "Service Search in Progress ("+transID+")"); synchronized (serviceSearchSemaphore) { serviceSearchSemaphore.wait(); } } catch (InterruptedException ie) { // 忽略 } catch (BluetoothStateException bse) { // ... System.out.println("Service Search Failed"); return; } } }

【移动开发小亮点】 JABWT

}

连接到一个服务 一旦一个感兴趣的服务被发现,客户端的应用程序可连接到它。就象你较早所学到的,客户

端可以从它的服务记录中收回服务连接 URL。下一个方法将展示如何连接一个服务器:

/** * 连接服务被服务记录代表 * @参数 sr 是感兴趣的服务的服务记录 */ public void btConnect(final ServiceRecord sr) { Thread th = new Thread() { public void run() { System.out.println("BTMIDlet.btConnect.run()"); RemoteDevice rd = sr.getHostDevice(); String connectionURL = sr.getConnectionURL( ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false); try { System.out.println( "Connecting to " + rd.getFriendlyName(false)); System.out.println( "Connecting to " + rd.getFriendlyName(false) + ", " + connectionURL); StreamConnection streamConnection = (StreamConnection) Connector.open(connectionURL); DataOutputStream dataout = streamConnection.openDataOutputStream(); dataout.writeUTF(messageOut); System.out.println("Closing"); streamConnection.close(); } catch (IOException ioe) { System.out.println( "BTMIDlet.btConnect, exception & + ioe); } } }; th.start(); }

这有另一个连接到服务器的途径。如果你不在乎哪一个设备提供想要的服务的话,你可以使

用 DiscoveryAgent.selectService().这个方法将搜索周围所有的蓝牙设备上 UUID 指示服务,

【移动开发小亮点】 JABWT

如果成功返回服务连接 URL。这是一个使用这个步骤的助理方法。

/** * 连接到 UUID 提供的服务器 * @参数 uuid 是 感兴趣服务的 UUID */ public void btConnect2(final UUID uuid) { Thread th = new Thread() { public void run() { System.out.println("BTMIDlet.btConnect2.run()"); try { // 选择服务指示 no // 确认或者 加密是必要的 String connectionURL = discoveryAgent.selectService(uuid,

ServiceRecord.NOAUTHENTICATE_NOENCRYPT,

false); mainScreen.setTitle( "Connecting to " + uuid.toString()); StreamConnection streamConnection = (StreamConnection) Connector.open(connectionURL); System.out.println("Sending message out"); DataOutputStream dataout = streamConnection.openDataOutputStream(); dataout.writeUTF(messageOut); System.out.println("Closing"); streamConnection.close(); } catch (BluetoothStateException bse) { System.out.println("BTMIDlet.btConnect2, exception " + bse); } catch (IOException ioe) { System.out.println("BTMIDlet.btConnect2, exception " + ioe); } } }; th.start(); }

注意:因为连接到一个服务器是一个长时间的操作,方法调度这个任务在它自身的执行线程。

【移动开发小亮点】 JABWT

锁定一个蓝牙连接 一个锁定蓝牙连接是一个被确认的,可授权的,经加密的连接。一个蓝牙连接可被锁定它被

创建时,或在稍后。 注意:不是所有的蓝牙实现都提供锁定连接。 当你创建一个蓝牙连接的锁定时,你必须确定 javax.microedition.io.Connector 连接

URL 的字符串 string 有了适当的安全参数: btspp://hostname:[CN | UUID];authenticate=true;authorize=true;encrypt=true ..其中 authenticate 验证一个连接设备的标识 authorize 验证它的访问一个已提供的服务.授权不被批准在客户端的 URL 连接字符串

strings.上 encrypt 规定连接必须被加密. 举例: btspp://localhost:2D26618601FB47C28D9F10B8EC891363;

authenticate=true;encrypt=true;name=MyBtService 一个客户端可以回收一个服务连接URL通过调用 ServiceRecord.getConnectionURL(). 这个方法参数中的一个 , requiredSecurity, 决定是否 返回连接 URL 将包括可选的 authenticate 和 encrypt 安全参数. 有效值对于 requiredSecurity 是: ServiceRecord.AUTHENTICATE_NOENCRYPT 表明 authenticate=true; encrypt=false. ServiceRecord.AUTHENTICATE_ENCRYPT 表明 authenticate=true; encrypt=true. 举例:

... ServiceRecord sr = ...; ... String connURL = sr.getConnectionURL( ServiceRecord.AUTHENTICATE_ENCRYPT, false); // 打开连接 StreamConnection sc = (StreamConnection) Connector.open(connURL); ...

你可以锁定一个连接在你通过调用 RemoteDeviece 锁定方法 authenticate(), authorize(), and encrypt(),举例:

确认一个远程设备:

StreamConnection sc = ...;

【移动开发小亮点】 JABWT

// 确认远程设备 通过指示连接 RemoteDevice rd = RemoteDevice.getRemoteDevice(sc); rd.authenticate();

授权一个远程设备访问一个被提供的服务:

// 授权客户端访问服务 由连接指示 StreamConnection sc = ...; RemoteDevice rd = RemoteDevice.getRemoteDevice(sc); rd.authorize(sc);

加密一个连接

// 加密连接 StreamConnection sc = ...; RemoteDevice rd = RemoteDevice.getRemoteDevice(sc); rd.encrypt(sc, true);

注意:确认必须被执行在授权和加密之前。如果在确认没有执行,就加密的话,会强制确认。

JABWT 和 MIDP 2.0 的安全性 MIDP 2.0 介绍了一个坚固的模式基于许可和规则上对于访问受限资源和 API。一个蓝牙应

用程序使用 JABWT 和运行在 MIDP2.0 之下将需要请求许可来使用网络资源。失败于请求

许可将可能促使 Connection.open()方法 抛出 SecurityException.例外。你请求许可可以通过

创建 MIDlet-Permissions 特性写进 JAD 文件和 JAR 的 manifest 中: 请求许可去打开一个蓝牙服务器的连接: Connector.open("btspp://localhost:..."):

MIDlet-Permissions:javax.microedition.io.Connector.bluetooth.server 请求许可去打开一个蓝牙客户端的连接: Connector.open("btspp://... "):

MIDlet-Permissions:javax.microedition.io.Connector.bluetooth.client 请求许可去打开服务器和客户端的连接: MIDlet-Permissions:javax.microedition.io.Connector.bluetooth.client,

【移动开发小亮点】 JABWT

javax.microedition.io.Connector.bluetooth.server

JABWT 和 MIDP2.0 的 PushRegistry MIDP 2.0's push registry 管理网络和计时器启动 MIDlet 的激活。-也就是说,它允许一个入

境的网络连接或者基于计时器的告警来唤醒一个 MIDlet 。因而 MIDlet 可以使用

PushRegistry 类来设置它们自身来自动运行。而不需要用户来启动,是否优先实现支持 push操作。 MIDlets 可通过引入蓝牙连接来激活,如果它们首次注册使用 push registry,静态或者动态的 对于静态注册,放置 MIDlet-Push-1 进 JAD 或 manifest:

... MIDlet-Push-1: btspp://localhost:2D26618601FB47C28D9F10B8EC891363; name=MyBtService,com.j2medeveloper.MyMIDlet, * ...

在运行时是动态注册的,使用 PushRegistry's registerConnection() API:

... // MIDlet 类名 String midletClassName = this.getClass().getName(); // 注册一个静态的连接. String url = "btspp://localhost:2D26618601FB47C28D9F10B8EC891363;name=MyBtService" // 使用一个无限制的过滤器. String filter = "*"; ... PushRegistry.registerConnection(url, midletClassName, filter); ...

MIDP 2.0 许可模型将请求应用程序来请求许可。创建以下的 MIDlet-Permissions 的特性写入

JAD 或 manifest 请求许可使用 PushRegistry 来激活通过一个蓝牙服务器连接:

MIDlet-Permissions: javax.microedition.io.PushRegistry, javax.microedition.io.PushRegistry.bluetooth.server

见文"The MIDP 2.0 Push Registry",学着如何使用PushRegistry API。

概要 你现在大概已对蓝牙网络和JSR82有了一个很好的理解了,将可以高效地使用JABWT(Java APIs for Bluetooth Wireless Technology)。对于允许个人设备共享数据和服务的个人局域网来

【移动开发小亮点】 JABWT

说,蓝牙是一个激动人心的无线技术。本文已涵盖了大部分的领域,包括一些关于蓝牙背景

信息,一个典型的基于蓝牙MIDlet的基本原理的概述,一个 javax.bluetooth 包里核心 JABWT接口的介绍,以及一些能使你知道如何使用这个核心 Java 蓝牙 API 的展示代码。

致谢 感谢 Jim Trudeau 和 Sony Ericsson 出借我已习惯于运行和测试本文样例代码的设备给我;

感谢 Roger Riggs 、 Kirill Kounik 和 Brian Christeson 的反馈和帮助改进本文。

资源 JABWT Specification (JSR 82)MIDP 2.0 Specification (JSR 118)

关于作者 C. Enrique Ortiz是一个软件设计师和开发者,同时是一个移动技术专家和作家。他是多个

出版发行物的作者/联合作者,是Sun微系统公司的移动Java开发认证考试的联合设计者,是

一位多个无线Java社群和J2ME专家组的积极参与者。Enrique 获有Puerto Rico大学的计算机

科学工学学士学位,有超过 15 年的软件工程,产品开发,和管理经验。