为Java ME程序添加蓝牙文本协议

这篇文章的副标题是:学习如何通过蓝牙PAN共享信息和事件。
移动设备在通讯和游戏工业持续的火爆,就像软件倾向于Ad Hoc和点对点网络一样,能处理不同种类设备的能力成为了网络应用程序(不论是游戏、生产还是信息共享)的一个很大优势。在这篇文章中,学习怎样在你的程 序中使用和集成蓝牙API(通过JSR82,已经引入了Java 2 平台微型版[j2me]中)。这里,你将会找到完整的蓝牙设备发现,配对,和消息通讯的实现。

被作为一种创新在移动设备领域持续扩展,及 越来越多的公司将芯片集成到他们更多的客户设备上(这不是传统意义上的计算关联),将这些设备连接起来组成一个智能环境的机会在增长。在这篇文章你会看到 基于蓝牙Java API的软件包如何使这些设备交换任意的基于文本的消息,以及简单的使用,对于客户端程序有用的API。


为什么是蓝牙?
蓝牙在构造个人区域网络,低数率连接设备,已经是一项成熟技术,廉价而且是无线的。蓝牙以多种方式(或类型)存在,以适应不同的电源和范围要求。这些特点才使得在移动设备之间和联网的静态设备之间建造Ad Hoc的点对点网络的想法出现,就像消费电子的客厅。

为什么使用PANs?
PANs 相对于LANs、WANs、和WWW,解决不同问题;PAN的问题通常由方便的方式驱动。多半被技术倾媚的是手机听筒和无键的车辆登录系统。也或者这些解 决方案需要蓝牙技术或者其它的无线技术,但倘若提供一个划算的方案,使得比标准的好很多,你就可以进入新市场了。

btevent软件包
要 将不同厂家的不同设备连接到一起,你需要一个简单的、灵活的通讯方式。像对象交换技术(OBEX)解决了二进制数据的传输问题,这对于很多蓝牙应用程序是 至关紧要的,但没有实现简单的消息协议。同样你需要知道设备和服务发现的概念,对是在设备之间建立Ad Hoc点对点连接的关键。这个brevents包提供一种简单、可重用技术在蓝牙设备之间实现文本通讯。
例如,你或许希望你的电视声音在电话响的时候自动调小。代码单1和2展示了如何利用btevents包在客户端程序中开启两个设备之间的通讯,而不需要大量的死板代码。
第一个要看的代码是运行在电话上。
Listing 1. Sample code for a telephone
package bluetooth.livingroom;
import com.ibm.btevents.*;
public class TelephoneMonitor extends MIDlet implements BTEventListener,
PhoneListener {
private BTManager btManager;
public TelephoneMonitor() {
btManager = new BTManager(this);
}
public void incomingCall(PhoneEvent event) {
btManager.sendMessage(
"bluetooth.livingroom.TelevisionMonitor",
"incomingCall:"+event.getCaller()
);
}
public void callEnded(PhoneEvent event) {
btManager.sendMessage(
"bluetooth.livingroom.TelevisionMonitor",
"callEnded:"+event.getDuration()
);
}
public void messageReceived(BTEvent event) {}
public void messageSent(BTEvent event) {}
public void devicesDiscovered(BTEvent event) {}
public void diagnosticMessage(BTEvent event) {}
}

TelevisionMonitor 类创建一个BTManager对象并注册一个BTEventListner对象。在这个案例中,电话并不关注蓝牙通讯中发生的事件;例如,在 TelephoneMonitor中。在这个类中比较有意思的代码在incomingCall() 和callEnded() 方法中。这些方法是被一个虚构的PhoneEvent时间调用的,这样能使TelevisionMonitor 通过BTManager对象传输消息。

现在,看看另外一边:
Listing 2. Sample code for a television
package bluetooth.livingroom;
import com.ibm.btevents.*;
public class TelevisionMonitor extends MIDlet implements BTEventListener {
private BTManager btManager;
public TelevisionMonitor() {
btManager = new BTManager(this);
}
private void reduceVolume() {
...
}
private void restoreVolume() {
...
}
private void displayMessage() {
...
}

public void messageReceived(BTEvent event) {
String message[] = event.getMessage().split(":");
if (message[0].equals("incomingCall")) {
displayMessage("Incoming call from "+message[1]);
reduceVolume();
} else if (message[1].equals("callEnded")) {
displayMessage("Call ended, lasted "+message[1]);
restoreVolume();
}

public void messageSent(BTEvent event) {}
public void devicesDiscovered(BTEvent event) {}
public void diagnosticMessage(BTEvent event) {}
}

这里客户端代码简单的接受消息。如果接收到了一个incomingCall消息,代码减小声音及在屏幕上显示来了个电话。如果接收到callEnded消息,这个代码恢复声音以及显示电话完成。
在 上面的每个代码快里,你需要假想关于设备相关的API和事件机制,例如TelephoneMonitor的PhoneEvent对象和 TelevisionMonitor在屏幕上显示消息和控制声音的功能。这些功能与设备特性相关,不可能在各种设备上调和。然而,你可以本地实现这些代 码;你不需要让电视意识到,或理解PhoneEvents。你只需要设备生产商承认设备代码之间的逻辑链接。在这个案例中,逻辑链接是电视希望知道用户什 么时候在打电话。

接着,看一下brevents如何处理消息。


btevents软件包介绍
btevents 包是建立在Java蓝牙接口上的。它使得脱离复杂,在不同设备之间,实现一个简单、基于文本的协议。就像名字所暗示的,信息是靠事件间提交的。因为 PANs可能是非常动态的,设备在它的范围进入和出去是没有提示的,使用它的所有软件必须遵从异步设计原则以防止暂停核挂起,并通过GUI提示用户。 Java语言的事件机制对于这个环境是一个完美的工具。

Figure 1 展示了btevents包的实现。

Figure 1. Package overview

TelephoneMonitor和TelevisionMonitor类都在他们的构造函数中创建了一个BTManager类的实例,因此你应该从研究BTManager类做了什么开始(列表3)。

Listing 3. The BTManager class
package com.ibm.btevents;
public class BTManager {
private BTTransmitter transmitter;
private BTReceiver receiver;
private BTDiscoverer discoverer;

public BTManager(BTEventListener listener) {
transmitter = new BTTransmitter();
transmitter.addBTEventListener(listener);
transmitter.start();
receiver = new BTReceiver();
receiver.addBTEventListener(listener);
receiver.start();
discoverer = new BTDiscoverer();
discoverer.addBTEventListener(listener);
discoverer.start();
}
public void startDiscovery() {
discoverer.searchForDevices();
}
public void sendMessage(String remoteName, String message)
throws DeviceNotFoundException {
transmitter.sendMessage(remoteName, message);
}
}
就像你所看到的,这个类创建和开始3个单独的线程,每个都有个不同的任务。在线程内部实现这些功能,消除了客户端程序在一个链接发送消息是被锁住的可能。

3个线程中的每个都实现了部分使两边能够通讯,就像设备和服务的发现。

BTDiscoverer

现在研究一下BTDiscoverer线程。这个线程允许客户端程序请求本地查找端点,使用BTEvent使得查找结果异步的返回。其中最重要的代码是初始化查找会话:

localDevice.getDiscoveryAgent().startInquiry(DiscoveryAgent.GIAC, this);

这个API调用自身的事件集,需要BTDiscoverer类实现DiscoveryListener接口。为了保持btevents接口干净和兼容,这些事件在内部处理。任何与发现相关的事件需要扩展到BTEvents并通过BTDiscover启动。

BTReceiver

BTReceiver 类是使用工作者线程模式设计的服务器,扮演着熟客的角色。它创建一个StreamConnectionNotifier 对象并监听,是否有连接进入。当BTReceiver发现一个连接,就通过StreamConnection对象离开工作者线程去处理输入和发出一个收到 消息BTEvent给所有的注册的BTEventListeners。

在两个蓝牙设备之间建立连接,StreamConnection不是唯一的选择。事实上有3种不同的连接方式,如下表

Table 1. Types of bluetooth connections
连接标志 具体名称

btgoep 通用对象交换
btl2cap 逻辑链路和适配层协议
btspp 模拟串口

第一个,btgoep传输二进制数据,所以他不适合基于字符串的通讯。btl2cap需要开发人员做低级的操作,配置最大消息单元。然而,btspp为开发人员隐藏了这些复杂性,因此选择逻辑连接是为了使开发人员的操作简单化。

连接URL通过javax.microedition.io.Connector.open(connURL) 调用的格式:

protected static final String BT_PROTOCOL = "btspp";protected static final String BT_ID = "1234";server = (StreamConnectionNotifier)Connector.open( BT_PROTOCOL + "://localhost:" + BT_ID);

对localhost的引用是很重要的,它告诉open()去创建一个服务器连接。如果你使用远程地址,你就创建了一个客户端连接。

BTTransmitter

BTTransmitter 类,就像名字所暗示的,负责传输消息到远程设备。客户端程序能通过BTManager类提交消息。这是通过BTTransmitter类并添加到一个消息 队列。BTTransmitter 线程监控这个队列并使用Connector类发送所有消息。要做到这个,它必须找到远程目标上的服务。这个过程很像设备发现。

localDevice.getDiscoveryAgent().searchServices( null, filter, message.getRemote(), this);

在查找完成后,你才能使用ServiceRecord去创建一个OoutputStream,接着可以写入一个消息:

out = Connector.openOutputStream( record.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false));out.write(message.getMessage().getBytes());fireMessageSentEvent(message.getMessage());

代码发送消息后,他会引发事件到监听器上。

应付PANs不可靠的天性,BTTransmitter线程将有限次重发发送失败的消息。在5次尝试后,就假设设备已经不在范围内了并丢弃消息--这是ad hoc 网络的天性。

使用这个开发包

现在回到感兴趣的工作:使设备相互交谈!你只需要很少的代码,就可以连接设备,运行brevents包。列表4展示了简单的伪代码。

Listing 4. Using the btevents package
import com.ibm.btevents.*;
public class MyBTClass implements BTEventListener {

...
private BTManager manager;

public MyBTClass() {
...
manager = new BTManager();
manager.addBTEventListener(this);
...
}
public void messageReceived(BTEvent event) {
...
}
public void messageSent(BTEvent event) {
...
}
public void devicesDiscovered(BTEvent event) {
...
}
public void diagnosticMessage(BTEvent event) {
...
}
public void errorMessage(BTEvent event) {
...
}
}

提供的软件包中包含一个简单的类com.ibm.btevents.ut.BTTestsMIDlet,它实现了一个简单的MIDlet。

结论

在这篇文章中,你已经知道如何在客户端程序中添加蓝牙传输层而不需要大量的样板代码和考虑异步消息的问题。你可以完成一个清晰的功能,并能使代码开发简单而且可维护。

原文链接

你可能感兴趣的:(java,游戏,网络协议,网络应用,IBM)