1. gzyueqian
      18529173453
      首頁 > 新聞中心 > > 正文

      J2ME藍牙實戰入門

      更新時間: 2007-05-16 13:54:43來源: 粵嵌教育瀏覽量:866


        目前,很多手機已經具備了藍牙功能。雖然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.如果該文能對你有所啟發,那就很好了。

      免費預約試聽課

      亚洲另类欧美综合久久图片区_亚洲中文字幕日产无码2020_欧美日本一区二区三区桃色视频_亚洲AⅤ天堂一区二区三区

      
      

      1. 亚洲欧美在线黑人 | 伊人久久大香线蕉精品 | 日本一本亚洲观看区 | 午夜福利视频入口 | 无遮挡粉嫩小泬久久久久久久 | 日韩精品亚洲人成在线观看 |