概述 目前,很多手機已經具備了藍牙功能。雖然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("
");
- }
- 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 {