服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > JAVA > 新手入门 > 基础入门 > 查看文档

j2me蓝牙实战入门

  目前,很多手机已经具备了蓝牙功能。虽然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.如果该文能对你有所启发,那就很好了。


  参考资料

1. http://developers.sun.com/techtopics/mobility/apis/articles/bluetoothintro/
 jsr82 api介绍(英文)
2. http://www.j2medev.com/article/showarticle.asp?articleid=249
 jsr82 api 介绍(中文)
3. http://www.jcp.org/en/jsr/detail?id=82
 jsr82 specification.
4.wtk22, bluetoothdemo 项目

扫描关注微信公众号