【 tulaoshi.com - 编程语言 】
                             
                              概述 目前,很多手机已经具备了蓝牙功能。虽然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.javaimport 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.javaimport 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;irecords.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("");         }         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.javaimport 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 {