0
View: 2106 |
开发技术 - 程序设
Author: 一滴蔚蓝色 |
Digg: 0
Date: 2007-07-29 |
计 |
概述
目前,很多手机已经具备了蓝牙功能。虽然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
1. 2. 3. 4. 5. 6. 7. 8. 9.
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; 10. /**
11. * @author Jagie 12. * 13. * MIDlet 14. */
15. public class StupidBTMIDlet extends MIDlet implements C
ommandListener { 16. List list; 17. ServerBox sb; 18. ClientBox cb; 19. /*
20. * (non-Javadoc) 21. *
22. * @see javax.microedition.midlet.MIDlet#startApp() 23. */
24. protected void startApp() throws MIDletStateChangeExc
eption {
25. list = new List(\"傻瓜蓝牙入门\", List.IMPLICIT); 26. list.append(\"Client\", null); 27. list.append(\"Server\", null); 28. list.setCommandListener(this);
29. Display.getDisplay(this).setCurrent(list); 30. } 31. 32. /**
33. * debug方法
34. * @param s 要显示的字串 35. */
36. public void showString(java/lang/String.java.html\"
target=\"_blank\">String s) {
37. Displayable dp = Display.getDisplay(this).getCurrent(); 38. Alert al = new Alert(null, s, null, AlertType.INFO); 39. al.setTimeout(2000);
40. Display.getDisplay(this).setCurrent(al, dp); 41. } 42. 43. /**
44. * 显示主菜单 45. * 46. */
47. public void showMainMenu() {
48. Display.getDisplay(this).setCurrent(list); 49. } 50.
51. protected void pauseApp() {
52. // TODO Auto-generated method stub 53. }
54. public void commandAction(Command com, Displayable d
isp) {
55. if (com == List.SELECT_COMMAND) { 56. List list = (List) disp;
57. int index = list.getSelectedIndex(); 58. if (index == 1) { 59. if (sb == null) {
60. sb = new ServerBox(this); 61. }
62. sb.setString(null);
63. Display.getDisplay(this).setCurrent(sb); . } else {
65. //每次都生成新的客户端实例 66. cb = null;
67. java/lang/System.java.html\"
target=\"_blank\">System.gc(); 68. cb = new ClientBox(this);
69. Display.getDisplay(this).setCurrent(cb); 70. } 71. } 72. }
73. protected void destroyApp(boolean arg0) throws MIDlet
StateChangeException {
74. // TODO Auto-generated method stub 75. } 76. }
ClientBox.java
1.
import java.io.java/io/DataInputStream.java.html\" target=\"_blank\">DataInputStream; 2.
import java.io.java/io/DataOutputStream.java.html\" target=\"_blank\">DataOutputStream; 3.
import java.io.java/io/IOException.java.html\" target=\"_blank\">IOException; 4.
import java.util.java/util/Vector.java.html\" target=\"_blank\">Vector; 5. 6. 7. 8. 9.
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;
10. import javax.microedition.lcdui.Form; 11. import javax.microedition.lcdui.Gauge; 12. import javax.microedition.lcdui.StringItem; 13. import javax.microedition.lcdui.TextField; 14. //jsr082 API
15. import javax.bluetooth.BluetoothStateException; 16. import javax.bluetooth.DeviceClass; 17. import javax.bluetooth.DiscoveryAgent; 18. import javax.bluetooth.DiscoveryListener; 19. import javax.bluetooth.LocalDevice;
20. import javax.bluetooth.RemoteDevice; 21. import javax.bluetooth.ServiceRecord; 22. import javax.bluetooth.UUID; 23. /**
24. * 客户端GUI 25. * @author Jagie 26. *
27. * TODO To change the template for this generated type com
ment go to
28. * Window - Preferences - Java - Code Style - Code Templates 29. */
30. public class ClientBox extends Form implements java/lan
g/Runnable.java.html\"
target=\"_blank\">Runnable, CommandListener, 31. DiscoveryListener { 32.
33. //字串输入框
34. TextField input = new TextField(null, \"\", 50, TextField.ANY
);
35. //loger
36. StringItem result = new StringItem(\"结果:\", \"\"); 37. private DiscoveryAgent discoveryAgent; 38.
39. private UUID[] uuidSet; 40. //响应服务的UUID
41. private static final UUID ECHO_SERVER_UUID = new UU
ID(
42. \"F0E0D0C0B0A000908070605040302010\", false); 43. //设备集合
44. java/util/Vector.java.html\"
target=\"_blank\">Vector devices = new java/util/Vector.java.html\" target=\"_blank\">Vector(); 45. //服务集合
46. java/util/Vector.java.html\"
target=\"_blank\">Vector records = new java/util/Vector.java.html\" target=\"_blank\">Vector(); 47.
48. //服务搜索的事务id集合 49. int[] transIDs;
50. StupidBTMIDlet midlet;
51. public ClientBox(StupidBTMIDlet midlet) { 52. super(\"\");
53. this.midlet=midlet; 54.
55. this.append(result); 56.
57. this.addCommand(new Command(\"取消
\",Command.CANCEL,1));
58. this.setCommandListener(this); 59.
60. new java/lang/Thread.java.html\"
target=\"_blank\">Thread(this).start(); 61. } 62.
63. public void commandAction(Command arg0, Displayable a
rg1) {
. if(arg0.getCommandType()==Command.CANCEL){ 65. midlet.showMainMenu();
66. }else{
67. //匿名内部Thread,访问远程服务。 68. java/lang/Thread.java.html\"
target=\"_blank\">Thread fetchThread=new java/lang/Thread.java.html\" target=\"_blank\">Thread(){ 69. public void run(){
70. for(int i=0;i mentAt(i); 72. if(accessService(sr)){ 73. //访问到一个可用的服务即可 74. break; 75. } 76. } 77. } 78. }; 79. fetchThread.start(); 80. } 81. 82. } 83. 84. 85. private boolean accessService(ServiceRecord sr){ 86. boolean result=false; 87. try { 88. java/lang/String.java.html\" target=\"_blank\">String url = sr.getConnectionURL( . ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false); 90. StreamConnection conn = (StreamConnection) Con nector.open(url); 91. 92. java/io/DataOutputStream.java.html\" target=\"_blank\">DataOutputStream dos=conn.openDataOutputStream(); 93. dos.writeUTF(input.getString()); 94. dos.close(); 95. java/io/DataInputStream.java.html\" target=\"_blank\">DataInputStream dis=conn.openDataInputStream(); 96. java/lang/String.java.html\" target=\"_blank\">String echo=dis.readUTF(); 97. dis.close(); 98. showInfo(\"反馈结果是:\"+echo); 99. result=true; 100. 101. } catch (java/io/IOException.java.html\" target=\"_blank\">IOException e) { 102. 103. } 104. return result; 105. } 106. public synchronized void run() { 107. //发现设备和服务的过程中,给用户以Gauge 108. Gauge g=new Gauge(null,false,Gauge.INDEFINITE,Ga uge.CONTINUOUS_RUNNING); 109. this.append(g); 110. showInfo(\"蓝牙初始化...\"); 111. boolean isBTReady = false; 112. try { 113. LocalDevice localDevice = LocalDevice.getLocalDevice (); 114. discoveryAgent = localDevice.getDiscoveryAgent(); 115. isBTReady = true; 116. } catch (java/lang/Exception.java.html\" target=\"_blank\">Exception e) { 117. e.printStackTrace(); 118. } 119. if (!isBTReady) { 120. showInfo(\"蓝牙不可用\"); 121. //删除Gauge 122. this.delete(1); 123. return; 124. } 125. uuidSet = new UUID[2]; 126. //标志我们的响应服务的UUID集合 127. uuidSet[0] = new UUID(0x1101); 128. uuidSet[1] = ECHO_SERVER_UUID; 129. 130. try { 131. discoveryAgent.startInquiry(DiscoveryAgent.GIAC, th is); 132. } catch (BluetoothStateException e) { 133. } 134. try { 135. //阻塞,由inquiryCompleted()回调方法唤醒 136. wait(); 137. } catch (java/lang/InterruptedException.java.html \" target=\"_blank\">InterruptedException e1) { 138. 139. e1.printStackTrace(); 140. } 141. showInfo(\"设备搜索完毕,共找到\"+devices.size()+\"个设备,开 始搜索服务\"); 142. transIDs = new int[devices.size()]; 143. for (int i = 0; i < devices.size(); i++) { 144. RemoteDevice rd = (RemoteDevice) devices.element At(i); 145. try { 146. //记录每一次服务搜索的事务id 147. transIDs[i] = discoveryAgent.searchServices(null, uuidSet, 148. rd, this); 149. } catch (BluetoothStateException e) { 150. continue; 151. } 152. } 153. 154. try { 155. //阻塞,由serviceSearchCompleted()回调方法在所有设备都搜索完的情况下唤醒 156. wait(); 157. } catch (java/lang/InterruptedException.java.html \" target=\"_blank\">InterruptedException e1) { 158. e1.printStackTrace(); 159. } 160. 161. showInfo(\"服务搜索完毕,共找到\"+records.size()+\"个服务,准 备发送请求\"); 162. if(records.size()>0){ 163. this.append(input); 1. this.addCommand(new Command(\"发送 \",Command.OK,0)); 165. } 166. 167. //删除Gauge 168. this.delete(1); 169. 170. } 171. 172. /** 173. * debug 174. * @param s 175. */ 176. 177. private void showInfo(java/lang/String.java.html\" target=\"_blank\">String s){ 178. java/lang/StringBuffer.java.html\" target=\"_blank\">StringBuffer sb=new java/lang/StringBuffer.java.html\" target=\"_blank\">StringBuffer(result.getText()); 179. if(sb.length()>0){ 180. sb.append(\"\\n\"); 181. } 182. sb.append(s); 183. result.setText(sb.toString()); 184. } 185. 186. /** 187. * 回调方法 188. */ 1. public void deviceDiscovered(RemoteDevice btDevice, De viceClass cod) { 190. if (devices.indexOf(btDevice) == -1) { 191. devices.addElement(btDevice); 192. } 193. } 194. /** 195. * 回调方法,唤醒初始化线程 196. */ 197. public void inquiryCompleted(int discType) { 198. synchronized (this) { 199. notify(); 200. } 201. } 202. /** 203. * 回调方法 204. */ 205. public void servicesDiscovered(int transID, ServiceRecord [] servRecord) { 206. for (int i = 0; i < servRecord.length; i++) { 207. records.addElement(servRecord[i]); 208. } 209. } 210. 211. /** 212. * 回调方法,唤醒初始化线程 213. */ 214. public void serviceSearchCompleted(int transID, int resp Code) { 215. 216. for (int i = 0; i < transIDs.length; i++) { 217. if (transIDs[i] == transID) { 218. transIDs[i] = -1; 219. break; 220. } 221. } 222. 223. //如果所有的设备都已经搜索服务完毕,则唤醒初始化线程。 224. boolean finished = true; 225. for (int i = 0; i < transIDs.length; i++) { 226. if (transIDs[i] != -1) { 227. finished = false; 228. break; 229. } 230. } 231. if (finished) { 232. synchronized (this) { 233. notify(); 234. } 235. } 236. } 237. } ServerBox.java 1. import java.io.java/io/DataInputStream.java.html\" target=\"_blank\">DataInputStream; 2. import java.io.java/io/DataOutputStream.java.html\" target=\"_blank\">DataOutputStream; 3. import java.io.java/io/IOException.java.html\" target=\"_blank\">IOException; 4. import java.util.java/util/Vector.java.html\" target=\"_blank\">Vector; 5. 6. 7. 8. 9. import javax.bluetooth.DiscoveryAgent; import javax.bluetooth.LocalDevice; import javax.bluetooth.ServiceRecord; import javax.bluetooth.UUID; import javax.microedition.io.Connector; 10. import javax.microedition.io.StreamConnection; 11. import javax.microedition.io.StreamConnectionNotifier; 12. import javax.microedition.lcdui.Command; 13. import javax.microedition.lcdui.CommandListener; 14. import javax.microedition.lcdui.Displayable; 15. import javax.microedition.lcdui.TextBox; 16. import javax.microedition.lcdui.TextField; 17. /** 18. * 服务端GUI 19. * @author Jagie 20. * 21. * TODO To change the template for this generated type com ment go to 22. * Window - Preferences - Java - Code Style - Code Templates 23. */ 24. public class ServerBox extends TextBox implements java /lang/Runnable.java.html\" target=\"_blank\">Runnable, CommandListener { 25. Command com_pub = new Command(\"开启服务 \", Command.OK, 0); 26. Command com_cancel = new Command(\"终止服务 \", Command.CANCEL, 0); 27. Command com_back = new Command(\"返回 \", Command.BACK, 1); 28. LocalDevice localDevice; 29. StreamConnectionNotifier notifier; 30. ServiceRecord record; 31. boolean isClosed; 32. ClientProcessor processor; 33. StupidBTMIDlet midlet; 34. //响应服务的uuid 35. private static final UUID ECHO_SERVER_UUID = new UU ID( 36. \"F0E0D0C0B0A000908070605040302010\", false); 37. public ServerBox(StupidBTMIDlet midlet) { 38. super(null, \"\", 500, TextField.ANY); 39. this.midlet = midlet; 40. this.addCommand(com_pub); 41. this.addCommand(com_back); 42. this.setCommandListener(this); 43. } 44. public void run() { 45. boolean isBTReady = false; 46. try { 47. localDevice = LocalDevice.getLocalDevice(); 48. if (!localDevice.setDiscoverable(DiscoveryAgent.GIAC )) { 49. showInfo(\"无法设置设备发现模式\"); 50. return; 51. } 52. // prepare a URL to create a notifier 53. java/lang/StringBuffer.java.html\" target=\"_blank\">StringBuffer url = new java/lang/StringBuffer.java.html\" target=\"_blank\">StringBuffer(\"btspp://\"); 54. // indicate this is a server 55. url.append(\"localhost\").append(:); 56. // add the UUID to identify this service 57. url.append(ECHO_SERVER_UUID.toString()); 58. // add the name for our service 59. url.append(\";name=Echo Server\"); 60. // request all of the client not to be authorized 61. // some devices fail on authorize=true 62. url.append(\";authorize=false\"); 63. // create notifier now . notifier = (StreamConnectionNotifier) Connector 65. .open(url.toString()); 66. record = localDevice.getRecord(notifier); 67. // remember weve reached this point. 68. isBTReady = true; 69. } catch (java/lang/Exception.java.html\" target=\"_blank\">Exception e) { 70. e.printStackTrace(); 71. 72. } 73. // nothing to do if no bluetooth available 74. if (isBTReady) { 75. showInfo(\"初始化成功,等待连接\"); 76. this.removeCommand(com_pub); 77. this.addCommand(com_cancel); 78. } else { 79. showInfo(\"初始化失败,退出\"); 80. return; 81. } 82. // 生成服务端服务线程对象 83. processor = new ClientProcessor(); 84. // ok, start accepting connections then 85. while (!isClosed) { 86. StreamConnection conn = null; 87. try { 88. conn = notifier.acceptAndOpen(); . } catch (java/io/IOException.java.html\" target=\"_blank\">IOException e) { 90. // wrong client or interrupted - continue anyway 91. continue; 92. } 93. processor.addConnection(conn); 94. } 95. } 96. public void publish() { 97. isClosed = false; 98. this.setString(null); 99. new java/lang/Thread.java.html\" target=\"_blank\">Thread(this).start(); 100. } 101. public void cancelService() { 102. isClosed = true; 103. showInfo(\"服务终止\"); 104. this.removeCommand(com_cancel); 105. this.addCommand(com_pub); 106. } 107. /* 108. * (non-Javadoc) 109. * 110. * @see javax.microedition.lcdui.CommandListener#comm andAction(javax.microedition.lcdui.Command, 111. * javax.microedition.lcdui.Displayable) 112. */ 113. public void commandAction(Command arg0, Displayable a rg1) { 114. if (arg0 == com_pub) { 115. //发布service 116. publish(); 117. } else if (arg0 == com_cancel) { 118. cancelService(); 119. } else { 120. cancelService(); 121. midlet.showMainMenu(); 122. } 123. } 124. 125. /** 126. * 内部类,服务端服务线程。 127. * @author Jagie 128. * 129. * TODO To change the template for this generated type co mment go to 130. * Window - Preferences - Java - Code Style - Code Templa tes 131. */ 132. private class ClientProcessor implements java/lang/Ru nnable.java.html\" target=\"_blank\">Runnable { 133. private java/lang/Thread.java.html\" target=\"_blank\">Thread processorThread; 134. private java/util/Vector.java.html\" target=\"_blank\">Vector queue = new java/util/Vector.java.html\" target=\"_blank\">Vector(); 135. private boolean isOk = true; 136. ClientProcessor() { 137. processorThread = new java/lang/Thread.java.ht ml\" target=\"_blank\">Thread(this); 138. processorThread.start(); 139. } 140. public void run() { 141. while (!isClosed) { 142. synchronized (this) { 143. if (queue.size() == 0) { 144. try { 145. //阻塞,直到有新客户连接 146. wait(); 147. } catch (java/lang/InterruptedException .java.html\" target=\"_blank\">InterruptedException e) { 148. } 149. } 150. } 151. 152. //处理连接队列 153. StreamConnection conn; 154. synchronized (this) { 155. if (isClosed) { 156. return; 157. } 158. conn = (StreamConnection) queue.firstElement( ); 159. queue.removeElementAt(0); 160. processConnection(conn); 161. } 162. } 163. } 1. 165. /** 166. * 往连接队列添加新连接,同时唤醒处理线程 167. * @param conn 168. */ 169. void addConnection(StreamConnection conn) { 170. synchronized (this) { 171. queue.addElement(conn); 172. notify(); 173. } 174. } 175. } 176. /** 177. * 从StreamConnection读取输入 178. * @param conn 179. * @return 180. */ 181. private java/lang/String.java.html\" target=\"_blank\">String readInputString(StreamConnection conn) { 182. java/lang/String.java.html\" target=\"_blank\">String inputString = null; 183. try { 184. java/io/DataInputStream.java.html\" target=\"_blank\">DataInputStream dis = conn.openDataInputStream(); 185. inputString = dis.readUTF(); 186. dis.close(); 187. } catch (java/lang/Exception.java.html\" target=\"_blank\">Exception e) { 188. e.printStackTrace(); 1. } 190. return inputString; 191. } 192. 193. /** 194. * debug 195. * @param s 196. */ 197. private void showInfo(java/lang/String.java.html\" target=\"_blank\">String s) { 198. java/lang/StringBuffer.java.html\" target=\"_blank\">StringBuffer sb = new java/lang/StringBuffer.java.html\" target=\"_blank\">StringBuffer(this.getString()); 199. if (sb.length() > 0) { 200. sb.append(\"\\n\"); 201. } 202. sb.append(s); 203. this.setString(sb.toString()); 204. } 205. 206. /** 207. * 处理客户端连接 208. * @param conn 209. */ 210. private void processConnection(StreamConnection conn) { 211. // 读取输入 212. java/lang/String.java.html\" target=\"_blank\">String inputString = readInputString(conn); 213. //生成响应 214. java/lang/String.java.html\" target=\"_blank\">String outputString = inputString.toUpperCase(); 215. //输出响应 216. sendOutputData(outputString, conn); 217. try { 218. conn.close(); 219. } catch (java/io/IOException.java.html\" target=\"_blank\">IOException e) { 220. } // ignore 221. showInfo(\"客户端输入:\" + inputString + \已成功响应!\"); 222. } 223. 224. /** 225. * 输出响应 226. * @param outputData 227. * @param conn 228. */ 229. private void sendOutputData(java/lang/String.java.ht ml\" target=\"_blank\">String outputData, StreamConnection conn) { 230. try { 231. java/io/DataOutputStream.java.html\" target=\"_blank\">DataOutputStream dos = conn.openDataOutputStream(); 232. dos.writeUTF(outputData); 233. dos.close(); 234. } catch (java/io/IOException.java.html\" target=\"_blank\">IOException e) { 235. } 236. } 237. } 小结 本文给出了一个简单的蓝牙服务的例子。旨在帮助开发者快速掌握JSR82.如果该文能对你有所启发,那就很好了。 参考资料 1. http://developers.sun.com/techtopics/mobility/apis/articles/bluetoothintro/ JSR82 API介绍(英文) 2. asp?ArticleID=249\">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 项目 作者简介 陈万飞,网名Jagie,培训师,爱好java技术.可通过chen_cwf@163.com与他联系
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- 517ttc.cn 版权所有 赣ICP备2024042791号-8
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务