[转载]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台蓝牙设备上安装后,一台设备作为服务端先运行,一台设备作为客户端后运行。在服务端上我们发布了一个服务,该服务的功能是把客户端发过来的字符串转变为大写字符串。客户端起动并搜索到服务端的服务后,我们就可以从客户端的输入框里输入任意的字符串,发送到服务端去,同时观察服务端的反馈结果。

实例代码

该程序包括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;i0){ 
            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 { 
            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("
"); 
        } 

        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。希望本文能对你有所启发。

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值