分享
 
 
 

BlueTooth探索系列(五)—J2ME蓝牙实战入门

王朝java/jsp·作者佚名  2008-05-31
窄屏简体版  字體: |||超大  

BlueTooth探索系列(五)—J2ME蓝牙实战入门

作者Jagie-(cleverpig的好朋友)

版权申明: 转载务必保留以下申明和链接

作者:Jagie

地址:http://www.matrix.org.cn/resource/article/43/43868_BlueTooth.Html

关键字:蓝牙,j2me,jsr82,入门

概述

目前,很多手机已经具备了蓝牙功能。虽然MIDP2.0没有包括蓝牙API,但是JCP定义了JSR82, Java APIs for Bluetooth Wireless Technology (JABWT).这是一个可选API,很多支持MIDP2.0的手机已经实现了,比如Nokia 6600, Nokia 6670,Nokia7610等等。对于一个开发者来说,如果目标平台支持JSR82的话,在制作联网对战类型游戏或者应用的时候,蓝牙是一个相当不错的选择。本文给出了一个最简单的蓝牙应用的J2ME程序,用以帮助开发者快速的掌握JSR82。该程序分别在2台蓝牙设备上安装后,一台设备作为服务端先运行,一台设备作为客户端后运行。在服务端上我们发布了一个服务,该服务的功能是把客户端发过来的字符串转变为大写字符串。客户端起动并搜索到服务端的服务后,我们就可以从客户端的输入框里输入任意的字符串,发送到服务端去,同时观察服务端的反馈结果。

本文并不具体讲述蓝牙的运行机制和JSR82的API结构,关于这些知识点,请参考本文的参考资料一节,这些参考资料会给你一个权威的精确的解释。

实例代码

该程序包括3个java文件。一个是MIDlet,另外2个为服务端GUI和客户端GUI。该程序已经在wtk22模拟器和Nokia 6600,Nokia 6670两款手机上测试通过。

stupidBTmidlet.java

import javax.microedition.lcdui.Alert;

import javax.microedition.lcdui.AlertType;

import javax.microedition.lcdui.Command;

import javax.microedition.lcdui.CommandListener;

import javax.microedition.lcdui.Display;

import javax.microedition.lcdui.Displayable;

import javax.microedition.lcdui.List;

import javax.microedition.midlet.MIDlet;

import javax.microedition.midlet.MIDletStateChangeException;

/**

* @author Jagie

*

* MIDlet

*/

public class StupidBTMIDlet extends MIDlet implements CommandListener {

List list;

ServerBox sb;

ClientBox cb;

/*

* (non-Javadoc)

*

* @see javax.microedition.midlet.MIDlet#startApp()

*/

protected void startApp() throws MIDletStateChangeException {

list = new List("傻瓜蓝牙入门", List.IMPLICIT);

list.append("Client", null);

list.append("Server", null);

list.setCommandListener(this);

Display.getDisplay(this).setCurrent(list);

}

/**

* debug方法

* @param s 要显示的字串

*/

public void showString(String s) {

Displayable dp = Display.getDisplay(this).getCurrent();

Alert al = new Alert(null, s, null, AlertType.INFO);

al.setTimeout(2000);

Display.getDisplay(this).setCurrent(al, dp);

}

/**

* 显示主菜单

*

*/

public void showMainMenu() {

Display.getDisplay(this).setCurrent(list);

}

protected void pauseApp() {

// TODO Auto-generated method stub

}

public void commandAction(Command com, Displayable disp) {

if (com == List.SELECT_COMMAND) {

List list = (List) disp;

int index = list.getSelectedIndex();

if (index == 1) {

if (sb == null) {

sb = new ServerBox(this);

}

sb.setString(null);

Display.getDisplay(this).setCurrent(sb);

} else {

//每次都生成新的客户端实例

cb = null;

System.gc();

cb = new ClientBox(this);

Display.getDisplay(this).setCurrent(cb);

}

}

}

protected void destroyApp(boolean arg0) throws MIDletStateChangeException {

// TODO Auto-generated method stub

}

}

clientbox.java

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.IOException;

import java.util.Vector;

import javax.microedition.io.Connector;

import javax.microedition.io.StreamConnection;

import javax.microedition.lcdui.Command;

import javax.microedition.lcdui.CommandListener;

import javax.microedition.lcdui.Displayable;

import javax.microedition.lcdui.Form;

import javax.microedition.lcdui.Gauge;

import javax.microedition.lcdui.StringItem;

import javax.microedition.lcdui.TextField;

//jsr082 API

import javax.bluetooth.BluetoothStateException;

import javax.bluetooth.DeviceClass;

import javax.bluetooth.DiscoveryAgent;

import javax.bluetooth.DiscoveryListener;

import javax.bluetooth.LocalDevice;

import javax.bluetooth.RemoteDevice;

import javax.bluetooth.ServiceRecord;

import javax.bluetooth.UUID;

/**

* 客户端GUI

* @author Jagie

*

* TODO To change the template for this generated type comment go to

* Window - Preferences - Java - Code Style - Code Templates

*/

public class ClientBox extends Form implements Runnable, CommandListener,

DiscoveryListener {

//字串输入框

TextField input = new TextField(null, "", 50, TextField.ANY);

//loger

StringItem result = new StringItem("结果:", "");

private DiscoveryAgent discoveryAgent;

private UUID[] uuidSet;

//响应服务的UUID

private static final UUID ECHO_SERVER_UUID = new UUID(

"F0E0D0C0B0A000908070605040302010", false);

//设备集合

Vector devices = new Vector();

//服务集合

Vector records = new Vector();

//服务搜索的事务id集合

int[] transIDs;

StupidBTMIDlet midlet;

public ClientBox(StupidBTMIDlet midlet) {

super("");

this.midlet=midlet;

this.append(result);

this.addCommand(new Command("取消",Command.CANCEL,1));

this.setCommandListener(this);

new Thread(this).start();

}

public void commandAction(Command arg0, Displayable arg1) {

if(arg0.getCommandType()==Command.CANCEL){

midlet.showMainMenu();

}else{

//匿名内部Thread,访问远程服务。

Thread fetchThread=new Thread(){

public void run(){

for(int i=0;i<records.size();i++){

ServiceRecord sr=(ServiceRecord)records.elementAt(i);

if(AccessService(sr)){

//访问到一个可用的服务即可

break;

}

}

}

};

fetchThread.start();

}

}

private boolean accessService(ServiceRecord sr){

boolean result=false;

try {

String url = sr.getConnectionURL(

ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);

StreamConnection conn = (StreamConnection) Connector.open(url);

DataOutputStream dos=conn.openDataOutputStream();

dos.writeUTF(input.getString());

dos.close();

DataInputStream dis=conn.openDataInputStream();

String echo=dis.readUTF();

dis.close();

showInfo("反馈结果是:"+echo);

result=true;

} catch (IOException e) {

}

return result;

}

public synchronized void run() {

//发现设备和服务的过程中,给用户以Gauge

Gauge g=new Gauge(null,false,Gauge.INDEFINITE,Gauge.CONTINUOUS_RUNNING);

this.append(g);

showInfo("蓝牙初始化...");

boolean isBTReady = false;

try {

LocalDevice localDevice = LocalDevice.getLocalDevice();

discoveryAgent = localDevice.getDiscoveryAgent();

isBTReady = true;

} catch (Exception e) {

e.printStackTrace();

}

if (!isBTReady) {

showInfo("蓝牙不可用");

//删除Gauge

this.delete(1);

return;

}

uuidSet = new UUID[2];

//标志我们的响应服务的UUID集合

uuidSet[0] = new UUID(0x1101);

uuidSet[1] = ECHO_SERVER_UUID;

try {

discoveryAgent.startInquiry(DiscoveryAgent.GIAC, this);

} catch (BluetoothStateException e) {

}

try {

//阻塞,由inquiryCompleted()回调方法唤醒

wait();

} catch (InterruptedException e1) {

e1.printStackTrace();

}

showInfo("设备搜索完毕,共找到"+devices.size()+"个设备,开始搜索服务");

transIDs = new int[devices.size()];

for (int i = 0; i < devices.size(); i++) {

RemoteDevice rd = (RemoteDevice) devices.elementAt(i);

try {

//记录每一次服务搜索的事务id

transIDs[i] = discoveryAgent.searchServices(null, uuidSet,

rd, this);

} catch (BluetoothStateException e) {

continue;

}

}

try {

//阻塞,由serviceSearchCompleted()回调方法在所有设备都搜索完的情况下唤醒

wait();

} catch (InterruptedException e1) {

e1.printStackTrace();

}

showInfo("服务搜索完毕,共找到"+records.size()+"个服务,准备发送请求");

if(records.size()>0){

this.append(input);

this.addCommand(new Command("发送",Command.OK,0));

}

//删除Gauge

this.delete(1);

}

/**

* debug

* @param s

*/

private void showInfo(String s){

StringBuffer sb=new StringBuffer(result.getText());

if(sb.length()>0){

sb.append("\n");

}

sb.append(s);

result.setText(sb.toString());

}

/**

* 回调方法

*/

public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {

if (devices.indexOf(btDevice) == -1) {

devices.addElement(btDevice);

}

}

/**

* 回调方法,唤醒初始化线程

*/

public void inquiryCompleted(int discType) {

synchronized (this) {

notify();

}

}

/**

* 回调方法

*/

public void servicesDiscovered(int transID, ServiceRecord[] servRecord) {

for (int i = 0; i < servRecord.length; i++) {

records.addElement(servRecord[i]);

}

}

/**

* 回调方法,唤醒初始化线程

*/

public void serviceSearchCompleted(int transID, int respCode) {

for (int i = 0; i < transIDs.length; i++) {

if (transIDs[i] == transID) {

transIDs[i] = -1;

break;

}

}

//如果所有的设备都已经搜索服务完毕,则唤醒初始化线程。

boolean finished = true;

for (int i = 0; i < transIDs.length; i++) {

if (transIDs[i] != -1) {

finished = false;

break;

}

}

if (finished) {

synchronized (this) {

notify();

}

}

}

}

serverbox.java

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.IOException;

import java.util.Vector;

import javax.bluetooth.DiscoveryAgent;

import javax.bluetooth.LocalDevice;

import javax.bluetooth.ServiceRecord;

import javax.bluetooth.UUID;

import javax.microedition.io.Connector;

import javax.microedition.io.StreamConnection;

import javax.microedition.io.StreamConnectionNotifier;

import javax.microedition.lcdui.Command;

import javax.microedition.lcdui.CommandListener;

import javax.microedition.lcdui.Displayable;

import javax.microedition.lcdui.TextBox;

import javax.microedition.lcdui.TextField;

/**

* 服务端GUI

* @author Jagie

*

* TODO To change the template for this generated type comment go to

* Window - Preferences - Java - Code Style - Code Templates

*/

public class ServerBox extends TextBox implements Runnable, CommandListener {

Command com_pub = new Command("开启服务", Command.OK, 0);

Command com_cancel = new Command("终止服务", Command.CANCEL, 0);

Command com_back = new Command("返回", Command.BACK, 1);

LocalDevice localDevice;

StreamConnectionNotifier notifier;

ServiceRecord record;

boolean isClosed;

ClientProcessor processor;

StupidBTMIDlet midlet;

//响应服务的uuid

private static final UUID ECHO_SERVER_UUID = new UUID(

"F0E0D0C0B0A000908070605040302010", false);

public ServerBox(StupidBTMIDlet midlet) {

super(null, "", 500, TextField.ANY);

this.midlet = midlet;

this.addCommand(com_pub);

this.addCommand(com_back);

this.setCommandListener(this);

}

public void run() {

boolean isBTReady = false;

try {

localDevice = LocalDevice.getLocalDevice();

if (!localDevice.setDiscoverable(DiscoveryAgent.GIAC)) {

showInfo("无法设置设备发现模式");

return;

}

// prepare a URL to create a notifier

StringBuffer url = new StringBuffer("btspp://");

// indicate this is a server

url.append("localhost").append(':');

// add the UUID to identify this service

url.append(ECHO_SERVER_UUID.toString());

// add the name for our service

url.append(";name=Echo Server");

// request all of the client not to be authorized

// some devices fail on authorize=true

url.append(";authorize=false");

// create notifier now

notifier = (StreamConnectionNotifier) Connector

.open(url.toString());

record = localDevice.getRecord(notifier);

// remember we've reached this point.

isBTReady = true;

} catch (Exception e) {

e.printStackTrace();

}

// nothing to do if no bluetooth available

if (isBTReady) {

showInfo("初始化成功,等待连接");

this.removeCommand(com_pub);

this.addCommand(com_cancel);

} else {

showInfo("初始化失败,退出");

return;

}

// 生成服务端服务线程对象

processor = new ClientProcessor();

// ok, start accepting connections then

while (!isClosed) {

StreamConnection conn = null;

try {

conn = notifier.acceptAndOpen();

} catch (IOException e) {

// wrong client or interrupted - continue anyway

continue;

}

processor.addConnection(conn);

}

}

public void publish() {

isClosed = false;

this.setString(null);

new Thread(this).start();

}

public void cancelService() {

isClosed = true;

showInfo("服务终止");

this.removeCommand(com_cancel);

this.addCommand(com_pub);

}

/*

* (non-Javadoc)

*

* @see javax.microedition.lcdui.CommandListener#commandAction(javax.microedition.lcdui.Command,

* javax.microedition.lcdui.Displayable)

*/

public void commandAction(Command arg0, Displayable arg1) {

if (arg0 == com_pub) {

//发布service

publish();

} else if (arg0 == com_cancel) {

cancelService();

} else {

cancelService();

midlet.showMainMenu();

}

}

/**

* 内部类,服务端服务线程。

* @author Jagie

*

* TODO To change the template for this generated type comment go to

* Window - Preferences - Java - Code Style - Code Templates

*/

private class ClientProcessor implements Runnable {

private Thread processorThread;

private Vector queue = new Vector();

private boolean isOk = true;

ClientProcessor() {

processorThread = new Thread(this);

processorThread.start();

}

public void run() {

while (!isClosed) {

synchronized (this) {

if (queue.size() == 0) {

try {

//阻塞,直到有新客户连接

wait();

} catch (InterruptedException e) {

}

}

}

//处理连接队列

StreamConnection conn;

synchronized (this) {

if (isClosed) {

return;

}

conn = (StreamConnection) queue.firstElement();

queue.removeElementAt(0);

processConnection(conn);

}

}

}

/**

* 往连接队列添加新连接,同时唤醒处理线程

* @param conn

*/

void addConnection(StreamConnection conn) {

synchronized (this) {

queue.addElement(conn);

notify();

}

}

}

/**

* 从StreamConnection读取输入

* @param conn

* @return

*/

private String readInputString(StreamConnection conn) {

String inputString = null;

try {

DataInputStream dis = conn.openDataInputStream();

inputString = dis.readUTF();

dis.close();

} catch (Exception e) {

e.printStackTrace();

}

return inputString;

}

/**

* debug

* @param s

*/

private void showInfo(String s) {

StringBuffer sb = new StringBuffer(this.getString());

if (sb.length() > 0) {

sb.append("\n");

}

sb.append(s);

this.setString(sb.toString());

}

/**

* 处理客户端连接

* @param conn

*/

private void processConnection(StreamConnection conn) {

// 读取输入

String inputString = readInputString(conn);

//生成响应

String outputString = inputString.toUpperCase();

//输出响应

sendOutputData(outputString, conn);

try {

conn.close();

} catch (IOException e) {

} // ignore

showInfo("客户端输入:" + inputString + ",已成功响应!");

}

/**

* 输出响应

* @param outputData

* @param conn

*/

private void sendOutputData(String outputData, StreamConnection conn) {

try {

DataOutputStream dos = conn.openDataOutputStream();

dos.writeUTF(outputData);

dos.close();

} catch (IOException e) {

}

}

}

小结

本文给出了一个简单的蓝牙服务的例子。旨在帮助开发者快速掌握JSR82.如果该文能对你有所启发,那就很好了。进入讨论组讨论。

(出处:http://www.knowsky.com)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有