[转载]J2ME蓝牙程序开发实战入门

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  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.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 {

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/374079/viewspace-131694/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/374079/viewspace-131694/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
简单j2me蓝牙OBEX传文件代码 http://blog.csdn.net/xiaoxiao108/archive/2011/03/10/6237233.aspx 最近想写一手机程序实现以下功能,在蓝牙的有效距离内,如果有其他手机的蓝牙设置为可见状态,自己的手机自动向其他手机发送指定的图片。查了下j2meapi发现通过obex对象交换协议比较容易实现。 实现代码很简单 写个程序 弄个死循环让手机一直搜索周围蓝牙设备,如果发现到周围有蓝牙设备,发送图片。 步骤如下 1.初始化蓝牙代码 LocalDevice local_device = LocalDevice.getLocalDevice(); DiscoveryAgent disc_agent = local_device.getDiscoveryAgent(); local_device.setDiscoverable(DiscoveryAgent.LIAC); 2.搜索周围的蓝牙设备 InquiryListener inq_listener = new InquiryListener(); disc_agent.startInquiry(DiscoveryAgent.LIAC, inq_listener); synchronized(inq_listener) { inq_listener.wait(); } 3.遍历所有蓝牙设备,查找每一个蓝牙设备的服务 while( devices.hasMoreElements() ) { synchronized(serv_listener) { RemoteDevice rd= (RemoteDevice)devices.nextElement(); t.setString(rd.getBluetoothAddress()); Thread.sleep(5000); t.setString(rd.getFriendlyName(true)); Thread.sleep(5000); disc_agent.searchServices(null, u,rd, serv_listener); serv_listener.wait(); } 4.从搜索到的ServiceRecord 中取出连接字符串进行连接 if (serv_listener.service!=null){ String url; url = serv_listener.service.getConnectionURL(0, false); Connection conn = Connector.open(url); ClientSession cs=(ClientSession)conn; cs.connect(null); 5.从资源中取出图片发送 byte filebytes[]=getImageData("/images/leaf.png"); HeaderSet hs=cs.createHeaderSet(); hs.setHeader(HeaderSet.NAME,"leaf.png"); hs.setHeader(HeaderSet.TYPE, "text/plain"); hs.setHeader(HeaderSet.LENGTH,new Long(filebytes.length)); Operation putOperation=cs.put(hs); OutputStream outputStream=putOperation.openOutputStream(); outputStream.write(filebytes); outputStream.close(); putOperation.close(); conn.close(); 6.设备查询类InquiryListener的代码 class InquiryListener implements DiscoveryListener{ public Vector cached_devices; public InquiryListener() { cached_devices = new Vector(); } public void deviceDiscovered( RemoteDevice btDevice, DeviceClass cod ) { if( ! cached_devices.contains( btDevice ) ) { cached_devices.addElement( btDevice ); } } public void inquiryCompleted( int discType ) { synchronized(this){ this.notify(); } } public void servicesDiscovered( int transID, ServiceRecord[] servRecord ) {} public void serviceSearchCompleted( int transID, int respCode ) {} } 7.服务查询类ServiceListener的代码 class ServiceListener implements DiscoveryListener{ public ServiceRecord service; public ServiceListener() { } public void servicesDiscovered( int transID, ServiceRecord[] servRecord ) { service = servRecord[0]; } public void serviceSearchCompleted( int transID, int respCode ) { synchronized( this ){ this.notify();} } public void deviceDiscovered( RemoteDevice btDevice, DeviceClass cod ){} public void inquiryCompleted( int discType ){} } 如果你发现有什么不合理的,需要改进的地方,联系328452421@qq.com 朱晓 (泰山学院)。相互交流 谢谢
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值