《Android NFC开发实战详解》——6.3节Android NFC P2P开发实例

    xiaoxiao2024-04-02  120

    本节书摘来自异步社区《Android NFC开发实战详解》一书中的第6章,第6.33节Android NFC P2P开发实例,作者 赵波,更多章节内容可以访问云栖社区“异步社区”公众号查看

    6.3 Android NFC P2P开发实例Android NFC开发实战详解学习了Android NFC P2P开发的基础知识后,本节将以程序实例的形式对Android NFC P2P功能进行进一步阐述,其中包括setNdefPushMessageCallback、setNdefPushMessage、enableForeground NdefPush以及结合AAR功能的Beam功能的四个实例开发。通过本节的学习,读者可以根据具体场景实现自己的P2P功能的开发。

    6.3.1 实例1:使用setNdefPushMessageCallback实现Android Beam在Android NFC P2P实例1中,对setNdefPushMessageCallback ( )方法实现Android Beam功能进行了实例描述。该实例包括消息发送端和接收端两部分,既可以完成NDEF消息的Push,又可以完成NDEF的获取。

    (1)BNM部分主要包括两个步骤,分别为:

    ① 在Activity中实现CreateNdefMessageCallback接口(implements);

    ② 在需要的地方调用setNdefPushMessageCallback();

    ③ 回调函数createNdefMessage(NfcEvent event)中实现Beam Data。

    (2)RBM部分主要包括两个步骤,分别为:

    ① 在Activity中重载onNewIntent(Intent intent),并在其中做setIntent(intent);

    ② 在Activity中重载onResume(),在其中做消息判别;

    ③ 当消息判别为需要的Beam时,处理接收的数据。

    具体参见实例代码中对应的注释,详细代码如下阐述。

    主程序P2PDemo1.java文件的代码如下:

    1. package skyseraph.nfc_demo.p2p.beam.app; //声明包 2. import java.nio.charset.Charset; //导入相关类 3. ……//该处省略了导入相关类的代码 4. public class P2PDemo1 extends Activity implements CreateNdefMessageCallback 5. { // BNM步骤1:在你的Activity中实现CreateNdefMessageCallback接口(implements) 6. private static final String Tag_ASSIST = "[P2PDemo1]-"; 7. private TextView p2pMessage = null; 8. private Context mContext = null; 9. // NFC相关 10. private NfcAdapter mNfcAdapter = null; 11. 12. @Override 13. protected void onCreate(Bundle savedInstanceState) 14. { 15. // TODO Auto-generated method stub 16. super.onCreate(savedInstanceState); 17. setContentView(R.layout.p2p_demo1); 18. mContext = this; 19. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "into onCreate"); 20. p2pMessage = (TextView) this.findViewById(R.id.p2p_demo1_tv); 21. checkNFCFunction(); 22. 23. p2pMessage.setText("Touch another mobile to Beam 'http://www.cnblogs. com/skyseraph/'or to Rev the beam msg"); 24. 25. // BNM步骤2: call setNdefPushMessageCallback anywhere your want 26. mNfcAdapter.setNdefPushMessageCallback(this, this); 27. } 28. 29. /* 30. * (non-Javadoc) 31. * 32. * @see 33. * android.nfc.NfcAdapter.CreateNdefMessageCallback#createNdefMessage (android 34. * .nfc.NfcEvent) 35. */ 36. // BNM步骤3:回调函数中实现Beam Data。 37. // 当发现有支持Beam的手机时,该回调接口会自动激活,你只需将你需要Beam的NDEF消息准备好 //并作为NdefMessage返回即可。本例中以RTD_URI为例。 38. @Override 39. public NdefMessage createNdefMessage(NfcEvent event) 40. { 41. // TODO Auto-generated method stub 42. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "into createNdefMessage"); 43. String uriFiledStr = "cnblogs.com/skyseraph/"; 44. Byte identifierCode = 0x01; 45. NdefMessage message = BobNdefMessage.getNdefMsg_from_RTD_URI(uriFiledStr, identifierCode, false); 46. return message; 47. } 48. 49. @Override 50. protected void onResume() 51. { 52. // TODO Auto-generated method stub 53. super.onResume(); 54. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "into onResume"); 55. // RBM步骤2:消息判别 56. if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) 57. { 58. // RBM步骤3:处理接收的消息/数据 59. resolveIntent(getIntent()); 60. } 61. } 62. 63. // RBM步骤1:onNewIntent setIntent(intent); 64. @Override 65. protected void onNewIntent(Intent intent) 66. { 67. // TODO Auto-generated method stub 68. // super.onNewIntent(intent); 69. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "into onNewIntent"); 70. setIntent(intent); 71. } 72. 73. // RBM步骤3:处理接收的数据 74. /** 75. * @param intent 76. */ 77. void resolveIntent(Intent intent) 78. { 79. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "into resolveIntent"); 80. String action = intent.getAction(); 81. if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) 82. { 83. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "ACTION_NDEF_DISCOVERED"); 84. NdefMessage[] messages = null; 85. Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter. EXTRA_NDEF_MESSAGES); 86. if (rawMsgs != null) 87. { 88. messages = new NdefMessage[rawMsgs.length]; 89. for (int i = 0; i < rawMsgs.length; i++) 90. { 91. messages[i] = (NdefMessage) rawMsgs[i]; 92. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "messages[i] = " + messages[i]); 93. } 94. } else 95. { 96. // Unknown tag type 97. byte[] empty = new byte[] 98. {}; 99. NdefRecord record = new NdefRecord(NdefRecord.TNF_UNKNOWN, empty, empty, empty); 100. NdefMessage msg = new NdefMessage(new NdefRecord[] 101. { record }); 102. messages = new NdefMessage[] 103. { msg }; 104. } 105. // Setup the views 106. setTitle(R.string.title_scanned_tag); 107. // process NDEF Msg 108. processNDEFMsg(messages); 109. } else if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) 110. { 111. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "ACTION_TECH_DISCOVERED"); 112. } else if (NfcAdapter.ACTION_Tag_DISCOVERED.equals(intent.getAction())) 113. { 114. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "ACTION_Tag_DISCOVERED"); 115. } else 116. { 117. LogUtil.e(MyConstant.Tag, Tag_ASSIST + "Unknown intent " + intent); 118. finish(); 119. return; 120. } 121. } 122. 123. /** 124. * 获取NdefMessage 125. * @param messages 126. */ 127. void processNDEFMsg(NdefMessage[] messages) 128. { 129. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "into processNDEFMsg"); 130. if (messages == null || messages.length == 0) 131. { 132. LogUtil.e(MyConstant.Tag, Tag_ASSIST +"NdefMessage is null"); 133. return; 134. } 135. for (int i = 0; i < messages.length; i++) 136. { 137. int length = messages[i].getRecords().length; 138. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "Message " + (i + 1) + "," + "length=" + length); 139. NdefRecord[] records = messages[i].getRecords(); 140. for (int j = 0; j < length; j++) // 几个记录 141. { 142. for (NdefRecord record : records) 143. { 144. if (isUri(record)) 145. { 146. parseUriRecord(record); 147. } 148. } 149. } 150. } 151. } 152. 153. /** 154. * 解析NdefMessage 155. * @param record 156. */ 157. private void parseUriRecord(NdefRecord record) 158. { 159. // TODO Auto-generated method stub 160. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "into parseUriRecord"); 161. short tnf = record.getTnf(); 162. if (tnf == NdefRecord.TNF_WELL_KNOWN) 163. { 164. parseWellKnownUriRecord(record); 165. } else if (tnf == NdefRecord.TNF_ABSOLUTE_URI) 166. { 167. parseAbsoluteUriRecord(record); 168. } else 169. { 170. LogUtil.e(MyConstant.Tag, Tag_ASSIST + "Unknown TNF " + tnf); 171. } 172. } 173. 174. private void parseAbsoluteUriRecord(NdefRecord record) 175. { 176. // TODO Auto-generated method stub 177. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "into parseAbsolute"); 178. byte[] payload = record.getPayload(); 179. Uri uri = Uri.parse(new String(payload, Charset.forName("UTF-8"))); 180. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record Tnf: " + record.getTnf() + "\n");// 1 181. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record type: " + new String(record.getType()) + "\n");// T 182. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record id: " + new String(record.getId()) + "\n"); 183. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record payload: " + uri + "\n"); 184. uiControl(uri); 185. } 186. 187. /** 188. * @param record 189. * 190. * payload[0] contains the URI Identifier Code, per the NFC Forum 191. * "URI Record Type Definition" section 3.2.2. 192. * 193. * payload[1]...payload[payload.length - 1] contains the rest of 194. * the URI. 195. */ 196. private void parseWellKnownUriRecord(NdefRecord record) 197. { 198. // TODO Auto-generated method stub 199. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "into parseWellKnown"); 200. Preconditions.checkArgument(Arrays.equals(record.getType(), NdefRecord. RTD_URI)); 201. byte[] payload = record.getPayload(); 202. 203. String prefix = URI_PREFIX_MAP.get(payload[0]); 204. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the prefix: " + prefix + "\n");// 205. byte[] fullUri = Bytes.concat(prefix.getBytes(Charset.forName("UTF-8")), 206. Arrays.copyOfRange(payload, 1, payload.length)); 207. Uri uri = Uri.parse(new String(fullUri, Charset.forName("UTF-8"))); 208. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record Tnf: " + record.getTnf() + "\n");// 209. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record type: " + new String (record.getType()) + "\n");// 210. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record id: " + new String (record.getId()) + "\n"); 211. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record payload: " + uri + "\n"); 212. uiControl(uri); 213. } 214. 215. /** 216. * UI操控 217. * 218. * @param uri 219. */ 220. private void uiControl(final Uri uri) 221. { 222. // TODO Auto-generated method stub 223. LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(Linear Layout.LayoutParams.WRAP_CONTENT, 224. LinearLayout.LayoutParams.WRAP_CONTENT); 225. Button button = new Button(this); 226. this.addContentView(button, params); 227. p2pMessage.setText("Rev MSG : " + "\n" + " " + uri); 228. button.setText("Open Link : " + uri); 229. button.setOnClickListener(new View.OnClickListener() 230. { 231. public void onClick(View view) 232. { 233. Intent data = new Intent(); 234. data.setAction(Intent.ACTION_VIEW); 235. data.setData(uri); 236. try 237. { 238. startActivity(data); 239. } catch (ActivityNotFoundException e) 240. { 241. return; 242. } 243. } 244. }); 245. } 246. 247. /** 248. * @param record 249. * @return 250. */ 251. public static boolean isUri(NdefRecord record) 252. { 253. if (record.getTnf() == NdefRecord.TNF_WELL_KNOWN) 254. { 255. if (Arrays.equals(record.getType(), NdefRecord.RTD_URI)) 256. { 257. return true; 258. } else 259. { 260. return false; 261. } 262. } else if (record.getTnf() == NdefRecord.TNF_ABSOLUTE_URI) 263. { 264. return true; 265. } else 266. { 267. return false; 268. } 269. } 270. 271. /** 272. * NFC Forum "URI Record Type Definition" 273. * 274. * This is a mapping of "URI Identifier Codes" to URI string prefixes, per 275. * section 3.2.2 of the NFC Forum URI Record Type Definition document. 276. */ 277. private static final BiMap<Byte, String> URI_PREFIX_MAP = ImmutableBiMap.<Byte, String> builder() 278. .put((byte) 0x00, "").put((byte) 0x01, "http://www.").put((byte) 0x02, "https://www.") 279. .put((byte) 0x03, "http://").put((byte) 0x04, "https://").put((byte) 0x05, "tel:") 280. .put((byte) 0x06, "mailto:").put((byte) 0x07, "ftp://anonymous: anonymous@").put((byte) 0x08, "ftp://ftp.") 281. .put((byte) 0x09, "ftps://").put((byte) 0x0A, "sftp://").put((byte) 0x0B, "smb://") 282. .put((byte) 0x0C, "nfs://").put((byte) 0x0D, "ftp://").put((byte) 0x0E, "dav://").put((byte) 0x0F, "news:") 283. .put((byte) 0x10, "telnet://").put((byte) 0x11, "imap:").put((byte) 0x12, "rtsp://") 284. .put((byte) 0x13, "urn:").put((byte) 0x14, "pop:").put((byte) 0x15, "sip:").put((byte) 0x16, "sips:") 285. .put((byte) 0x17, "tftp:").put((byte) 0x18, "btspp://").put((byte) 0x19, "btl2cap://") 286. .put((byte) 0x1A, "btgoep://").put((byte) 0x1B, "tcpobex://").put ((byte) 0x1C, "irdaobex://") 287. .put((byte) 0x1D, "file://").put((byte) 0x1E, "urn:epc:id:").put ((byte) 0x1F, "urn:epc:tag:") 288. .put((byte) 0x20, "urn:epc:pat:").put((byte) 0x21, "urn:epc:raw:"). put((byte) 0x22, "urn:epc:") 289. .put((byte) 0x23, "urn:nfc:").build(); 290. 291. /** 292. * NFC Function Check By skyseraph 2013-2 293. */ 294. private void checkNFCFunction() 295. { 296. // TODO Auto-generated method stub 297. mNfcAdapter = NfcAdapter.getDefaultAdapter(this); 298. if (mNfcAdapter == null) 299. { 300. p2pMessage.setText("NFC apdater is not available"); 301. Dialog dialog = null; 302. CustomDialog.Builder customBuilder = new CustomDialog.Builder (mContext); 303. customBuilder.setTitle(getString(R.string.inquire)).setMessage (getString(R.string.nfc_notice2)) 304. .setIcon(R.drawable.dialog_icon2) 305. .setNegativeButton(getString(R.string.no), new Dialog Interface.OnClickListener() 306. { 307. public void onClick(DialogInterface dialog, int which) 308. { 309. dialog.dismiss(); 310. finish(); 311. } 312. }).setPositiveButton(getString(R.string.yes), new Dialog Interface.OnClickListener() 313. { 314. public void onClick(DialogInterface dialog, int which) 315. { 316. dialog.dismiss(); 317. finish(); 318. } 319. }); 320. dialog = customBuilder.create(); 321. dialog.setCancelable(false);// back 322. dialog.setCanceledOnTouchOutside(false); 323. SetDialogWidth(dialog).show(); 324. return; 325. } else 326. { 327. if (!mNfcAdapter.isEnabled()) 328. { 329. Dialog dialog = null; 330. CustomDialog.Builder customBuilder = new CustomDialog.Builder (mContext); 331. customBuilder.setTitle(getString(R.string.inquire)).setMessage(getString(R.strin g.nfc_notice3)) 332. .setIcon(R.drawable.dialog_icon2) 333. .setNegativeButton(getString(R.string.no), new DialogInterface.OnClickListener() 334. { 335. public void onClick(DialogInterface dialog, int which) 336. { 337. dialog.dismiss(); 338. finish(); 339. } 340. }).setPositiveButton(getString(R.string.yes), new DialogInterface.OnClickListener() 341. { 342. public void onClick(DialogInterface dialog, int which) 343. { 344. dialog.dismiss(); 345. Intent setnfc = new Intent(Settings. ACTION_WIRELESS_SETTINGS); 346. // Intent setnfc = new 347. // Intent(Settings.ACTION_NFC_SETTINGS); 348. startActivity(setnfc); 349. } 350. }); 351. dialog = customBuilder.create(); 352. dialog.setCancelable(false);// back 353. dialog.setCanceledOnTouchOutside(false); 354. SetDialogWidth(dialog).show(); 355. return; 356. } else if (!mNfcAdapter.isNdefPushEnabled()) 357. { 358. Intent setnfc = new Intent(Settings.ACTION_NFCSHARING_ SETTINGS); 359. startActivity(setnfc); 360. return; 361. } 362. } 363. } 364. 365. /** 366. * @param dialog 367. * @return 368. */ 369. private Dialog SetDialogWidth(Dialog dialog) 370. { 371. DisplayMetrics dm = new DisplayMetrics(); 372. // 取得窗口属性 373. getWindowManager().getDefaultDisplay().getMetrics(dm); 374. // 窗口的宽度 375. int screenWidth = dm.widthPixels; 376. // 窗口高度 377. int screenHeight = dm.heightPixels; 378. WindowManager.LayoutParams params = dialog.getWindow().getAttributes(); 379. if (screenWidth > screenHeight) 380. { 381. params.width = (int) (((float) screenHeight) * 0.875); 382. 383. } else 384. { 385. params.width = (int) (((float) screenWidth) * 0.875); 386. } 387. dialog.getWindow().setAttributes(params); 388. 389. return dialog; 390. } 391. }

    第4行为BNM步骤1阶段,即实现CreateNdefMessageCallback接口。实现该接口后,在该Activity中需重载createNdefMessage (NfcEvent event)函数,在该函数中返回需要的Beam数据。第21行为实现NFC功能的检测,在使用Android Beam功能前需要确保设备支持NFC功能。NFC功能可用,且Android Beam功能是enable,具体可通过isEnabled() 和 isNdefPushEnabled()函数实现,参考代码第291~363行checkNFCFunction()函数。第26行为BNM步骤2阶段,即调用setNdefPushMessageCallback(this, this),实现该接口后,当发现有其他设备Beam数据,该Activity中会接收一个回调,createNdefMessage (NfcEvent event)函数会自动激活。第29~47行为BNM步骤3阶段,即在回调函数createNdefMessage (NfcEvent event)中实现Beam Data。在createNdefMessage (NfcEvent event)函数中,其通过调用setNdefPushMessageCallback (this, this) 后自动激活,其中,可以创建需要的NDEF消息并将其返回。第45行为通过调用BobNdefMessage.getNdefMsg_from_RTD_URI(String uriFiledStr, byte identifierCode, boolean flagAddAAR)方法生成RTD-URI类型的NDEF消息,也可以创建其他类型的NDEF消息。关于BobNdefMessage类可参考第5章。第63~71行为RBM步骤1阶段,即在Activity中重载onNewIntent(Intent intent),并在其中setIntent(intent)。第49~61行为RBM步骤2阶段,即在Activity中重载onResume(),并在其中做消息判别。第59行为RBM步骤3阶段,即当消息判别为需要的Beam时,调用resolveIntent(Intent intent)函数处理接收的数据,resolveIntent(Intent intent)函数可参考第73~121行所示。第73~121行为resolveIntent(Intent intent)函数,处理接收到的NDEF消息。其中,第85行通过intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)获取具体的消息数据,然后在第86行判断该消息是否为空。不为空时,将其依次保存到定义的NdefMessage[]中;为空时,NdefMessage[]消息中保存一个默认的NdefRecord.TNF_UNKNOWN记录类型。第108行为处理resolveIntent(Intent intent)中得到的NdefMessage[]消息,具体函数参考第123~151所示。其中,首先检测NdefMessage[]消息是否为空,再依次处理每个消息(第135行)和每个消息中的每个记录(第140~142行)。第144行isUri(NdefRecord record)函数主要完成判别当前记录是否为URI类型,具体见第247~269行所述。第146行parseUriRecord(NdefRecord record)为解析获取的消息记录,具体见第153~172行所述。第247~269行为isUri(NdefRecord record)函数,完成判别当前记录是否为URI类型。由于NFC消息记录中定义的URI类型有两种,即NdefRecord.TNF_WELL_KNOW(第253行)N和NdefRecord.TNF_ABSOLUTE_URI(第262行),因此此处分别作了判断。其中,记录格式和类型的分别通过record.getTnf()和record.getType()得到。第153~172行为parseUriRecord(NdefRecord record)函数,完成对当前获取的记录进行解析得到最终的数据(Payload)。如上所述,此处也分两种情况,通过getTnf()得到记录格式(第161行),然后再针对NdefRecord.TNFWELL_KNOWN和NdefRecord.TNF_ABSOLUTE URI分别解析以获取Payload。两者的解析分别参考第174~185行和第187~213行的parseAbsoluteUriRecord (NdefRecord record)和parseWellKnownUriRecord(NdefRecord record)函数,具体解析过程需参考NFC论坛定义URI相关协议,在第5章已描述,此处省略。第215~245行为uiControl(final Uri uri)函数,主要完成对解析完后的NDEF消息的Payload(即URI)进行UI映射。此处通过一个文本控件显示该URI内容,同时增添一个按钮控件并添加点击响应函数。当用户点击该按钮时,跳转到文本控件中显示的URI链接主页上,如图6-5(c)所示。第271~289行为利用Google Collections Library BiMap 定义的URI_PREFIX_MAP,用于parseWellKnownUriRecord(NdefRecord record)函数中(第187~213行)中URI的前缀的判别。具体根据NFC论坛定义的协议文档“URI Record Type Definition”所述。BobNdefMessage为自定义NdefMessage辅助类,具体代码可参考第5章的5.3.2节,此处省略。

    LogUtil为自定义调试类,主要是为方便APP在发布正式版本时一次性关闭所有调试Log信息,具体代码可参考第5章的5.2.2小节,此处省略。

    MyConstant为自定义常量类,具体代码可参考第5章的5.2.2小节,此处省略。

    在布局文件中,p2p_demo1.xml包含了一个按钮控件和一个文本控件,并修改了相关属性,代码如下:

    1. <?xml version="1.0" encoding="utf-8"?> 2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3. android:layout_width="fill_parent" 4. android:layout_height="fill_parent" 5. android:gravity="center" 6. android:orientation="vertical" > 7. 8. <Button 9. android:layout_width="match_parent" 10. android:layout_height="wrap_content" 11. android:layout_gravity="center" 12. android:clickable="false" 13. android:paddingTop="20dp" 14. android:text="NDEF Msg Push by setNdefPushMessageCallback" /> 15. 16. <TextView 17. android:id="@+id/p2p_demo1_tv" 18. android:layout_width="match_parent" 19. android:layout_height="wrap_content" 20. android:paddingTop="20dp" 21. android:text="p2p_demo1" 22. android:textSize="20dp" /> 23. </LinearLayout>

    第8~14行为按钮控件,此处的按钮控件只是起提示作用。第16~23行为文本控件,文本控件主要显示需要Beam的数据以及接收到的Beam数据。在AndroidManifest.xml中声明Activity,并添加NFC权限,其代码如下:

    1. <uses-permission android:name="android.permission.NFC" /> 2. <activity 3. android:name="skyseraph.nfc_demo.p2p.beam.app.P2PDemo1" 4. android:label="NFC_Demo_P2P-1" > 5. <intent-filter> 6. <action android:name="android.intent.action.MAIN" /> 7. <category android:name="android.intent.category.skyseraph_nfc_demo" /> 8. </intent-filter> 9. <!-- Add RTD-URI filter --> 10. <intent-filter> 11. <action android:name="android.nfc.action.NDEF_DISCOVERED" /> 12. <category android:name="android.intent.category.DEFAULT" /> 13. <data 14. android:host="*" 15. android:pathPrefix="" 16. android:scheme="http" /> 17. </intent-filter> 18. </activity>

    第1行为APP添加NFC权限。第10~17行增加一个RTD-URI过滤器,以便能够接受任何来自其他NFC设备的Scheme为http的URI。Beam文件传输实例1的具体效果如图6-3至图6-5所示,其中,图6-3所示为两台准备了Beam的手机。打开本实例APP,然后将两台手机触屏,如图6-4(a)所示。Push成功后,作为RBM的手机将显示刚刚输入的信息,如图6-4(b)和图6-5(a)所示。点击RBM端接收到的URI链接信息,此时将实现跳转。当用户手机中存在多个可以打开URI链接的APP时,系统会提示用户进行选择,如图6-5(b)所示。若用户手机只有唯一一个能打开URI链接的APP,将直接进行跳转,跳转的结果如图6-5(c)所示。

    6.3.2 实例2:使用setNdefPushMessage实现Android Beam在Android NFC P2P实例2中,本书对setNdefPushMessage ( )方法实现Android Beam功能进行了实例描述。该实例包括消息发送端和接收端两部分,既可以完成NDEF消息的Push,又可以完成NDEF的获取。详细代码如下。

    (1)BNM部分主要包括两个步骤,分别为:

    ① 创建NDEF消息;

    ② 在需要的地方调用setNdefPushMessage( )方法。

    (2)RBM部分主要包括两个步骤,分别为:

    ① 在Activity中重载onNewIntent(Intent intent),并在其中做setIntent(intent);

    ② 在Activity中重载onResume(),在其中做消息判别;

    ③ 当消息判别为需要的Beam时,处理接收的数据。

    具体参见实例代码中对应的注释,详细代码如下阐述。

    主程序P2PDemo2.java文件代码如下:

    1. package skyseraph.nfc_demo.p2p.beam.app; //声明包 2. import java.nio.charset.Charset; //导入相关类 3. ……//该处省略了导入相关类的代码 4. public class P2PDemo2 extends Activity 5. { 6. private static final String Tag_ASSIST = "[P2PDemo2]-"; 7. private Context mContext = null; 8. // UI相关 9. private TextView mTextView = null; 10. private EditText mEditText = null; 11. private Button mButton = null; 12. private String inputText = null; 13. // NFC相关 14. private NfcAdapter mNfcAdapter = null; 15. 16. @Override 17. protected void onCreate(Bundle savedInstanceState) 18. { 19. // TODO Auto-generated method stub 20. super.onCreate(savedInstanceState); 21. setContentView(R.layout.p2p_demo2); 22. mContext = this; 23. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "into onCreate"); 24. initUI(); 25. checkNFCFunction(); 26. initFunction(); 27. } 28. 29. private void initUI() 30. { 31. // TODO Auto-generated method stub 32. mTextView = (TextView) this.findViewById(R.id.p2p_demo2_tv); 33. mEditText = (EditText) this.findViewById(R.id.p2p_demo2_et); 34. mButton = (Button) this.findViewById(R.id.p2p_demo2_btn); 35. } 36. 37. private void initFunction() 38. { 39. // TODO Auto-generated method stub 40. mButton.setOnClickListener(new OnClickListener() 41. { 42. @Override 43. public void onClick(View v) 44. { 45. // TODO Auto-generated method stub 46. // BNM步骤1: Create NDEF Msg 47. // get the input message your want to push 48. getInputMessage(); 49. if (inputText == null || inputText.isEmpty()) 50. { 51. inputText = "This is an RTD_TEXT from P2PDemo2"; 52. LogUtil.w(MyConstant.Tag, Tag_ASSIST + "inputText is null"); 53. } 54. // change the message to NDEF to Push 55. NdefMessage message = BobNdefMessage.getNdefMsg_from_RTD_ TEXT(inputText, false, false); 56. 57. // BNM步骤2:call setNdefPushMessage anywhere your want 58. mNfcAdapter.setNdefPushMessage(message, P2PDemo2.this); 59. Toast.makeText(mContext, "Touch another mobile to share the message:" + inputText, Toast.LENGTH_SHORT) 60. .show(); 61. } 62. }); 63. } 64. 65. /** 66. * getInputMessage() 67. */ 68. private void getInputMessage() 69. { 70. // TODO Auto-generated method stub 71. inputText = mEditText.getText().toString(); 72. } 73. 74. @Override 75. protected void onResume() 76. { 77. // TODO Auto-generated method stub 78. super.onResume(); 79. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "into onResume"); 80. // RBM步骤2:消息判别 81. if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) 82. { 83. // RBM步骤3:处理接收的消息/数据 84. resolveIntent(getIntent()); 85. } 86. } 87. 88. // RBM步骤1:onNewIntent setIntent(intent); 89. @Override 90. protected void onNewIntent(Intent intent) 91. { 92. // TODO Auto-generated method stub 93. // super.onNewIntent(intent); 94. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "into onNewIntent"); 95. setIntent(intent); 96. } 97. 98. /** 99. * RBM步骤3:处理接收的数据* 100. * @param intent 101. */ 102. void resolveIntent(Intent intent) 103. { 104. // Android NFC P2P实例1中的resolveIntent(Intent intent)函数,该处省略 105. } 106. 107. /** 108. * 获取NdefMessage 109. * 110. * @param messages 111. */ 112. void processNDEFMsg(NdefMessage[] messages) 113. { 114. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "into processNDEFMsg"); 115. if (messages == null || messages.length == 0) 116. { 117. LogUtil.e(MyConstant.Tag, Tag_ASSIST + "NdefMessage is null"); 118. return; 119. } 120. for (int i = 0; i < messages.length; i++) 121. { 122. int length = messages[i].getRecords().length; 123. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "Message " + (i + 1) + "," + "length=" + length); 124. NdefRecord[] records = messages[i].getRecords(); 125. for (int j = 0; j < length; j++) // 几个记录 126. { 127. for (NdefRecord record : records) 128. { 129. if (isTextRecord(record)) 130. { 131. parseRTD_TEXTRecord(record); 132. } 133. } 134. } 135. } 136. } 137. 138. /** 139. * @param record 140. * @return 141. */ 142. public static boolean isTextRecord(NdefRecord record) 143. { 144. if (record.getTnf() == NdefRecord.TNF_WELL_KNOWN) 145. { 146. if (Arrays.equals(record.getType(), NdefRecord.RTD_TEXT)) 147. { 148. return true; 149. } else 150. { 151. return false; 152. } 153. } else 154. { 155. return false; 156. } 157. } 158. 159. 160. /** 161. * @param record 162. * payload[0] contains the "Status Byte Encodings" field, per the 163. * NFC Forum "Text Record Type Definition" section 3.2.1. 164. * 165. * bit7 is the Text Encoding Field. 166. * 167. * if (Bit_7 == 0): The text is encoded in UTF-8 if (Bit_7 == 1): 168. * The text is encoded in UTF16 169. * 170. * Bit_6 is reserved for future use and must be set to zero. 171. * 172. * Bits 5 to 0 are the length of the IANA language code. 173. */ 174. void parseRTD_TEXTRecord(NdefRecord record) 175. { 176. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "into parseRTD_TEXTRecord"); 177. // 记录格式验证 178. Preconditions.checkArgument(record.getTnf() == NdefRecord.TNF_WELL_KNOWN); 179. // 记录类型验证 180. Preconditions.checkArgument(Arrays.equals(record.getType(), NdefRecord. RTD_TEXT)); 181. 182. String payloadStr = ""; 183. byte[] payload = record.getPayload(); // 获取记录payload内容 184. Byte statusByte = record.getPayload()[0];// 获取记录payload第1个字节 185. 186. String textEncoding = ((statusByte & 0200) == 0) ? "UTF-8" : "UTF-16"; // 0x80=0200 ,获取状态字节编码 187. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "textEncoding = " + textEncoding); 188. int languageCodeLength = statusByte & 0077; // & 0x3F=0077(bit 5 to 0) 189. // 获取语言码长度 190. String languageCode = new String(payload, 1, languageCodeLength, Charset.forName("UTF-8")); // 获取语言码 191. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "languageCodeLength = " + languageCodeLength + ",languageCode = "+ languageCode); 192. 193. try 194. { 195. payloadStr = new String(payload, languageCodeLength + 1, payload. length - languageCodeLength - 1, textEncoding); // 获取payload实际数据 196. 197. } catch (UnsupportedEncodingException e) 198. { 199. // TODO Auto-generated catch block 200. e.printStackTrace();// 异常信息 201. } 202. 203. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record Tnf: " + record.getTnf()+ "\n");// 1 204. // ,状态字节编码信息 205. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record type: " + new String (record.getType()) + "\n");// T,语言码信息 206. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record id: " + new String (record.getId()) + "\n");// 记录ID信息 207. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record payload: " + payloadStr + "\n");// 解析获取到的payload实际数据 208. mTextView.setText("New Msg Rev(Text): " + payloadStr); 209. } 210. 211. /** 212. * NFC Function Check By skyseraph 2013-2 213. */ 214. private void checkNFCFunction() 215. { 216. // Android NFC P2P实例1中的checkNFCFunction ()函数,该处省略 217. } 218. 219. /** 220. * @param dialog 221. * @return 222. */ 223. private Dialog SetDialogWidth(Dialog dialog) 224. { 225. // Android NFC P2P实例1中的SetDialogWidth ()函数,该处省略 226. } 227. }

    第24行为UI初始化阶段,主要通过findViewById完成UI控件的初始化(第29~35行)。第25行为实现NFC功能的检测,在使用Android Beam功能前需要确保设备支持NFC功能、NFC功能可用,且Android Beam功能是enable。具体可通过isEnabled() 和 isNdefPushEnabled()函数实现,参考代码第211~217行checkNFCFunction()函数。第26行为调用按钮控件事件响应函数,函数中完成按钮控件的事件监听(第37~63行)。第46~55行为BNM步骤1阶段,即创建NDEF消息阶段。创建消息前先通过第65~72行的getInputMessage()函数获取来自编辑框用户输入的待Beam to Share的信息。如果用户未输入,程序会自动添加一个默认值(第49~53行),得到需要用户待Beam的信息后,再调用getNdefMsg_from_RTD_TEXT(String RTD_TEXT, boolean encodeInUtf8, boolean flagAddAAR)方法(第55行)生成RTD-Text类型的NDEF消息。关于BobNdefMessage类可参考第5章。第58行为BNM步骤2阶段,即调用setNdefPushMessage (message, P2PDemo2.this),在该函数中静态的传入需要Beam的NDEF消息。第88~96行为RBM步骤1阶段,即在Activity中重载onNewIntent(Intent intent),并在其中做setIntent(intent)。第74~86行为RBM步骤2阶段,即在Activity中重载onResume(),并在其中做消息判别。第84行为RBM步骤3阶段,即当消息判别为需要的Beam时,调用resolveIntent(Intent intent)函数处理接收的数据,resolveIntent(Intent intent)函数可参考第98~105行所示。第98~105行为resolveIntent(Intent intent)函数。该函数与Android NFC P2P实例1中的resolveIntent(Intent intent)函数相同,具体可参考对应内容。第107~136行为处理resolveIntent(Intent intent)中得到的NdefMessage[]消息的函数。其中,首先检测NdefMessage[]消息是否为空,然后再依次处理每个消息(第120行)和每个消息中的每个记录(第125~128行)。第129行调用的isTextRecord(record)函数主要完成判别当前记录是否为Text类型,具体见第138~157行所述,然后在第131行调用的parseRTD_TEXTRecord(record)函数来解析获取的消息记录,具体见第160~209行所述。第138~157行为isTextRecord(NdefRecord record)函数,完成判别当前记录是否为Text类型。当为Text类型时,返回true,反之返回false。其中,记录格式和类型的分别获取通过record.getTnf()和record.getType()得到。第160~209行为parseRTD_TEXTRecord(NdefRecord record)函数,完成对当前获取的记录进行解析得到最终的数据(Payload)。详细的解析过程参考代码中的注释信息,同时参考NFC论坛定义URI相关协议(在第5章已描述,此处省略)。BobNdefMessage为自定义NdefMessage辅助类,具体代码可参考第5章的5.3.2节,此处省略。

    LogUtil为自定义调试类,主要是为方便APP在发布正式版本时一次性关闭所有调试Log信息。具体代码可参考第5章的5.2.2节,此处省略。

    MyConstant为自定义常量类,具体代码可参考第5章的5.2.2节,此处省略。

    布局文件p2p_demo2.xml中包含了一个按钮控件、一个输入编辑框控件和一个文本控件,并修改了相关属性,代码如下:

    1. <?xml version="1.0" encoding="utf-8"?> 2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3. android:layout_width="fill_parent" 4. android:layout_height="fill_parent" 5. android:orientation="vertical" > 6. 7. <TextView 8. android:id="@+id/p2p_demo2_tv" 9. android:layout_width="match_parent" 10. android:layout_height="wrap_content" 11. android:paddingTop="20dp" 12. android:textSize="15dp" /> 13. 14. <EditText 15. android:id="@+id/p2p_demo2_et" 16. android:layout_width="match_parent" 17. android:layout_height="wrap_content" 18. android:ems="10" 19. android:hint="Input what your want to share in here!" 20. android:inputType="text" 21. android:paddingTop="20dp" > 22. <requestFocus /> 23. </EditText> 24. 25. <Button 26. android:id="@+id/p2p_demo2_btn" 27. android:layout_width="wrap_content" 28. android:layout_height="wrap_content" 29. android:layout_gravity="center" 30. android:paddingTop="20dp" 31. android:text="NDEF Msg Push by setNdefPushMessage" /> 32. 33. </LinearLayout>

    第7~12行为文本控件,主要显示需要Beam的数据以及接收到的Beam数据。第14~23行为编辑框控件,作为Beam to Share文本的输入。第25~31行为按钮控件,按住开始Beam数据。AndroidManifest.xml中声明Activity,并添加NFC权限,其代码如下:``

    android:name="skyseraph.nfc_demo.p2p.beam.app.P2PDemo2"android:label="NFC_Demo_P2P-2" > ``

    第1行为APP添加NFC权限。第10~15行增加一个RTD-Text过滤器,以便能够接受任何来自其他NFC设备的Text数据。Beam文件传输实例1的具体效果如图6-6~图6-8所示。其中,图6-8所示为两台准备了Beam的手机。打开本实例APP,其中要作为BNM端的手机中输入待前台Push的消息,点击NDEF Msg Push By setNdefPushMessage按钮,如图6-7(a)所示,然后将两台手机触屏,如图6.7(b)所示。同时触摸BNM实现发送,如图6.7(c)所示。Push成功后,作为RBM的手机将显示你刚刚输入的信息,如图6-8所示。

    6.3.3 实例3:使用enableForegroundNdefPush实现Android Beam在Android NFC P2P实例3中,本书对enableForegroundNdef Push( )方法实现Android Beam功能进行了实例描述。该实例包括消息发送端和接收端两部分,既可以完成NDEF消息的Push,又可以完成NDEF的获取,详细代码如下。

    主程序P2PDemo3.java文件代码如下:``

    package skyseraph.nfc_demo.p2p.beam.app; //声明包import java.nio.charset.Charset; //导入相关类……//该处省略了导入相关类的代码public class P2PDemo3 extends Activity{private static final String Tag_ASSIST = "[P2PDemo3]-";private Context mContext = null;// UI相关private TextView mTextView = null;private EditText mEditText = null;private Button mButton = null;private String inputText = null;// NFC相关private NfcAdapter mNfcAdapter = null; @Overrideprotected void onCreate(Bundle savedInstanceState){// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);setContentView(R.layout.p2p_demo3);mContext = this;LogUtil.i(MyConstant.Tag, Tag_ASSIST + "into onCreate");initUI();checkNFCFunction();initFunction();} private void initUI(){// TODO Auto-generated method stubmTextView = (TextView) findViewById(R.id.p2p_demo3_tv);mEditText = (EditText) findViewById(R.id.p2p_demo3_et);mButton = (Button) findViewById(R.id.p2p_demo3_bt);}private void initFunction(){// TODO Auto-generated method stubmButton.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View v){// TODO Auto-generated method stub// BNM步骤1:准备NDEF数据// get the input message your want to pushgetInputMessage();if (inputText == null || inputText.isEmpty()){inputText = "This is an RTD_TEXT from P2PDemo3";LogUtil.w(MyConstant.Tag, Tag_ASSIST + "inputText is null");}// change the message to NDEF to PushmNdefMessage = BobNdefMessage.getNdefMsg_from_RTD_TEXT(input Text,false,false); // BNM步骤2:enableForegroundNdefPushif (null != mNdefMessage){mNfcAdapter.enableForegroundNdefPush(P2PDemo3.this, mNdefMessage);Toast.makeText(P2PDemo3.this, "Touch another NFC device to share this message", Toast.LENGTH_SHORT).show();}}});}/** getInputMessage()*/private void getInputMessage(){// TODO Auto-generated method stubinputText = mEditText.getText().toString();} @Overrideprotected void onResume(){// TODO Auto-generated method stubsuper.onResume();// RBM步骤2:消息判别if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())){resolveIntent(getIntent());} else{// BNM步骤4:enableForegroundNdefPushmNfcAdapter.enableForegroundNdefPush(this, BobNdefMessage.getNdef Msg_from_RTD_TEXT("",false,false));}} @Overrideprotected void onPause(){// TODO Auto-generated method stubsuper.onPause();// BNM步骤3:disableForegroundNdefPushif (null != mNfcAdapter)mNfcAdapter.disableForegroundNdefPush(this);} // RBM步骤1:onNewIntent setIntent(intent);@Overrideprotected void onNewIntent(Intent intent){// TODO Auto-generated method stub// super.onNewIntent(intent);setIntent(intent);}/** RBM步骤3:处理接收的数据* @param intent*/void resolveIntent(Intent intent){// Android NFC P2P实例1中的resolveIntent(Intent intent)函数,该处省略} /** 获取NdefMessage @param messages*/void processNDEFMsg(NdefMessage[] messages){// Android NFC P2P实例2中的processNDEFMsg (NdefMessage[] messages)函数,该处省略} /** @param record @return*/public static boolean isTextRecord(NdefRecord record){// Android NFC P2P实例2中的isTextRecord (NdefRecord record)函数,该处省略} /** @param record,另一种方法*/void parseRTD_TEXTRecord(NdefRecord record){LogUtil.i(MyConstant.Tag, Tag_ASSIST + "into parseRTD_TEXTRecord2");String payload = "";String recordType = new String(record.getType()); // Byte to// StringLogUtil.i(MyConstant.Tag, Tag_ASSIST + "recordType:" + recordType); // String// compare!if (!recordType.equals("T")){LogUtil.e(MyConstant.Tag, Tag_ASSIST + "not RTD-Text,return!");return;}Byte statusByte = record.getPayload()[0];int languageCodeLength = statusByte & 0x3F;LogUtil.i(MyConstant.Tag, Tag_ASSIST + "Language Code Length:" + language CodeLength + "n");// 2String languageCode = new String(record.getPayload(), 1, languageCode Length, Charset.forName("UTF-8"));

    LogUtil.i(MyConstant.Tag, Tag_ASSIST + "Language Code:" + languageCode

    "n"); // enint isUTF8 = statusByte - languageCodeLength;if (isUTF8 == 0x00){LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record is UTF-8n");//payload = new String(record.getPayload(), 1 + languageCodeLength, record.getPayload().length - 1 languageCodeLength, Charset.forName("UTF-8"));} else if (isUTF8 == -0x80){LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record is UTF-16n");payload = new String(record.getPayload(), 1 + languageCodeLength, record.getPayload().length - 1 languageCodeLength, Charset.forName("UTF-16"));}LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record Tnf: " + record.getTnf()+ "n");// 1LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record type: " + new String (record.getType()) + "n");// TLogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record id: " + new String (record.getId()) + "n");

    LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record payload: " + payload

    "n");mTextView.setText("New Msg Rev(Text): " + payload);}/** NFC Function Check By skyseraph 2013-2*/private void checkNFCFunction(){// Android NFC P2P实例1中的checkNFCFunction ()函数,该处省略} /** @param dialog @return*/private Dialog SetDialogWidth(Dialog dialog){// Android NFC P2P实例1中的SetDialogWidth ()函数,该处省略}}``

    第24行为UI初始化阶段,主要通过findViewById完成UI控件的初始化(第29~35行)。第25行为实现NFC功能的检测,在使用Android Beam功能前需要确保设备支持NFC功能、NFC功能可用,且Android Beam功能是enable。具体可通过isEnabled() 和 isNdefPushEnabled()函数实现,参考代码第174~180行checkNFCFunction()函数。第26行为调用按钮控件事件响应函数,函数中完成按钮控件的事件监听(第36~64行)。第46~54行为BNM步骤1阶段,即准备NDEF消息阶段。创建NDEF消息前先通过第65~72行的getInputMessage()函数获取来自编辑框用户输入的待Beam to Share的信息。如果用户未输入,程序会自动添加一个默认值(第48~52行),得到需要用户待Beam的信息后,再调用getNdefMsg_from_RTD_TEXT(String RTD_TEXT, boolean encodeInUtf8, boolean flagAddAAR)方法(第54行),生成RTD-Text类型的NDEF消息。关于BobNdefMessage类可参考第5章的5.3.2节。第58行为BNM步骤2阶段,即调用enableForegroundNdefPush (P2PDemo3.this, mNdefMessage),在该函数中的传入需要Push的NDEF消息。该方法创建后,message处于挂起状态,一旦系统检测到RBM设备,该message就会通过Beam传输给接收端。第90~98行为BNM步骤3阶段,即在onPause( )方法中,调用disableForegroundNdef Push(this)方法。由于这是一种前台推送方法,因此,一旦Activity不出于前台,就需要Foreground NDEF Push立即停止。第85~86行为BNM步骤4阶段,即在onResume ( )方法中,调用enableForegroundNdefPush (this)方法。当应用再次处于前台时,可以通过该方法首次或再次启用Foreground NDEF Push推送。第100~107行为RBM步骤1阶段,即在Activity中重载onNewIntent(Intent intent),并在其中做setIntent(intent)。第80行为RBM步骤2阶段,即在Activity中重载onResume(),并在其中做消息判别。第82行为RBM步骤3阶段,即当消息判别为需要的Beam时,调用resolveIntent(Intent intent)函数处理接收的数据。关于resolveIntent(Intent intent)函数可参考第108~115行所示。第108~115行为resolveIntent(Intent intent)函数。该函数与Android NFC P2P实例1中的resolveIntent(Intent intent)函数相同,具体可参考对应内容。第122~125行为处理resolveIntent(Intent intent)中得到的NdefMessage[]消息的函数。该函数与Android NFC P2P实例2中的processNDEFMsg (NdefMessage[] messages)函数相同,具体可参考对应内容。第131~134行为isTextRecord(NdefRecord record)函数,完成判别当前记录是否为Text类型。该函数与Android NFC P2P实例2中的isTextRecord (NdefRecord record)函数相同,具体可参考对应内容。第138~173行为parseRTD_TEXTRecord(NdefRecord record)函数,完成对当前获取的记录进行解析得到最终的数据(Payload)。详细的解析过程参考代码中的注释信息,同时参考NFC论坛定义URI相关协议(在第5章已描述,此处省略)。BobNdefMessage为自定义NdefMessage辅助类,具体代码可参考第5章的5.3.2节,此处省略。

    LogUtil为自定义调试类,主要是为方便APP在发布正式版本时一次性关闭所有调试Log信息,具体代码可参考第5章的5.2.2节,此处省略。

    MyConstant为自定义常量类,具体代码可参考第5章的5.2.2节,此处省略。

    布局文件p2p_demo3.xml中包含了一个按钮控件、一个输入编辑框控件和一个文本控件,并修改了相关属性,代码如下:``

    <?xml version="1.0" encoding="utf-8"?> android:layout_width="fill_parent"android:layout_height="fill_parent"android:orientation="vertical" > android:id="@+id/p2p_demo3_tv"android:layout_width="match_parent"android:layout_height="wrap_content"android:paddingTop="20dp" /> android:id="@+id/p2p_demo3_et"android:layout_width="match_parent"android:layout_height="wrap_content"android:ems="12"android:inputType="text"android:paddingTop="20dp" > android:id="@+id/p2p_demo3_bt"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:paddingTop="20dp"android:text="Foreground NDEF Msg Push" /> 第7~11行为文本控件,主要显示需要Push NDEF的数据以及接收到的Push数据。

    第13~21行为编辑框控件,作为Foreground NDEF Push文本的输入。第23~30行为按钮控件,按住开始Push NDEF数据。AndroidManifest.xml中声明Activity,并添加NFC权限,其代码如下:

    android:name="skyseraph.nfc_demo.p2p.beam.app.P2PDemo3"android:label="NFC_Demo_P2P-3" > 第1行为APP添加NFC权限。 第10~15行增加一个RTD-Text过滤器,以便能够接受任何来自其他NFC设备Text数据。 Beam文件传输实例1的具体效果如图6.9~图6.11所示。其中,图6.9所示为两台准备了Beam的手机。打开本实例APP,其中,要作为BNM端的手机中输入待前台Push的消息,点击Foreground NDEF Msg Push按钮,如图6.10(a)所示。然后将两台手机触屏如图6.10(b)所示。Push成功后,作为RBM的手机将显示刚刚输入的信息,如图6.11所示。 <div style="text-align: center"> <img src="https://yqfile.alicdn.com/4b00c2c5b562bd35a5dc68f3cf3757c57f6b248f.png" > </div> <div style="text-align: center"> <img src="https://yqfile.alicdn.com/efe41b7237fe6a75a3c2f58f1bf48fe7ae326c58.png" > </div> **6.3.4 实例4:结合AAR实现Android Beam** 在第4章中详细介绍了AAR相关知识。Android NFC P2P实例4 中结合了AAR和 setNdefPush MessageCallback(Android NFC P2P实例1)来实现Android Beam,同时还增加了对BNM后的结果进行了处理。主要代码与实例1相似,详细代码如下: package skyseraph.nfc_demo.p2p.beam.app; //声明包import java.nio.charset.Charset; //导入相关类……//该处省略了导入相关类的代码public class P2PDemo4 extends Activity implements CreateNdefMessageCallback, OnNdefPushCompleteCallback{ // BNM步骤1:在你的Activity中实现CreateNdefMessageCallback接口(implements)private static final String Tag_ASSIST = "[P2PDemo4]-";private Context mContext = null;// UI相关private TextView mTextView = null;// NFC相关private NfcAdapter mNfcAdapter = null;private static final int MESSAGE_SENT = 1; @Overrideprotected void onCreate(Bundle savedInstanceState){// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);setContentView(R.layout.p2p_demo4);mContext = this;LogUtil.i(MyConstant.Tag, Tag_ASSIST + "into onCreate");mTextView = (TextView) this.findViewById(R.id.p2p_demo4_tv);checkNFCFunction();mTextView.setText("Touch another mobile to Beam 'http://www.cnblogs. com/skyseraph/' or to Rev the beam msg with AAR"); // BNM步骤2: call setNdefPushMessageCallback anywhere your wantmNfcAdapter.setNdefPushMessageCallback(this, this);// Register callback to set NDEF message // BNM步骤4:获取Beam发送状态mNfcAdapter.setOnNdefPushCompleteCallback(this, this);// Register callback to listen for message-sent success} /* (non-Javadoc) @see android.nfc.NfcAdapter.OnNdefPushCompleteCallback#onNdefPushComplete( android.nfc.NfcEvent)*/// BNM步骤4:获取Beam发送状态(API 14)@Overridepublic void onNdefPushComplete(NfcEvent event){// TODO Auto-generated method stubLogUtil.w(MyConstant.Tag, Tag_ASSIST + "into onNdefPushComplete");mHandler.obtainMessage(MESSAGE_SENT).sendToTarget();} /* (non-Javadoc) @see android.nfc.NfcAdapter.CreateNdefMessageCallback#createNdefMessage(android .nfc.NfcEvent)*/// BNM步骤3:回调函数中实现Beam Data。// 当发现有支持Beam的手机时,该回调接口会自动激活,你只需将你需要Beam的NDEF消息准备好 // 并作为NdefMessage返回即可。// 本例中以RTD_URI为例。@Overridepublic NdefMessage createNdefMessage(NfcEvent event){// TODO Auto-generated method stubLogUtil.i(MyConstant.Tag, Tag_ASSIST + "into createNdefMessage");String uriFiledStr = "cnblogs.com/skyseraph/";Byte identifierCode = 0x01;NdefMessage message = BobNdefMessage.getNdefMsg_from_RTD_URI(uriFiled Str, identifierCode, true);// AAR设置为truereturn message;} @Overrideprotected void onResume(){// Android NFC P2P实例1中的onResume()函数,该处省略} // RBM步骤1:onNewIntent setIntent(intent);@Overrideprotected void onNewIntent(Intent intent){// Android NFC P2P实例1中的onNewIntent(Intent intent)函数,该处省略} /** RBM步骤3:处理接收的数据 @param intent*/void resolveIntent(Intent intent){// Android NFC P2P实例1中的resolveIntent(Intent intent)函数,该处省略} /** 获取NdefMessage @param messages*/void processNDEFMsg(NdefMessage[] messages){// Android NFC P2P实例1中的processNDEFMsg(NdefMessage[] messages)函数,该处省略} /** 解析NdefMessage @param record*/private void parseUriRecord(NdefRecord record){// Android NFC P2P实例1中的parseUriRecord(NdefRecord record)函数,该处省略} private void parseAbsoluteUriRecord(NdefRecord record){// Android NFC P2P实例1中的parseAbsoluteUriRecord(NdefRecord record)函数,该处省略} /** @param record payload[0] contains the URI Identifier Code, per the NFC Forum "URI Record Type Definition" section 3.2.2. payload[1]...payload[payload.length - 1] contains the rest of the URI.*/private void parseWellKnownUriRecord(NdefRecord record){// Android NFC P2P实例1中的parseWellKnownUriRecord(NdefRecord record)函数,该处省略} /** UI操控 @param uri*/private void uiControl(final Uri uri){// Android NFC P2P实例1中的uiControl(final Uri uri)函数,该处省略} /** @param record @return*/public static boolean isUri(NdefRecord record){// Android NFC P2P实例1中的isUri(NdefRecord record)函数,该处省略 } /** NFC Forum "URI Record Type Definition" This is a mapping of "URI Identifier Codes" to URI string prefixes, per section 3.2.2 of the NFC Forum URI Record Type Definition document.*/private static final BiMap URI_PREFIX_MAP = ImmutableBiMap. String> builder()// Android NFC P2P实例1中的URI_PREFIX_MAP,该处省略 /** This handler receives a message from onNdefPushComplete*/private final Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg){switch (msg.what){case MESSAGE_SENT:Toast.makeText(getApplicationContext(), "Message sent!", Toast.LENGTH_LONG).show();break;}}}; /** NFC Function Check By skyseraph 2013-2*/private void checkNFCFunction(){// Android NFC P2P实例1中的checkNFCFunction()函数,该处省略} /** @param dialog @return*/private Dialog SetDialogWidth(Dialog dialog){// Android NFC P2P实例1中的SetDialogWidth ()函数,该处省略}} 第4行为BNM步骤1阶段,即实现CreateNdefMessageCallback接口。实现该接口后,该Activity中需重载createNdefMessage (NfcEvent event)函数,在该函数中返回需要的Beam数据。 第23行为实现NFC功能的检测,在使用Android Beam功能前需要确保设备支持NFC功能、NFC功能可用,且Android Beam功能是enable。具体可通过isEnabled() 和 isNdefPushEnabled()函数实现,参考代码第180~183行checkNFCFunction()函数。 第28行为BNM步骤2阶段,即调用setNdefPushMessageCallback(this, this)。实现该接口后,当发现有其他设备的Beam数据,该Activity中会接收一个回调,createNdefMessage (NfcEvent event)函数会自动激活。 第61~69行为BNM步骤3阶段,即在回调函数createNdefMessage (NfcEvent event)中实现Beam Data。在createNdefMessage (NfcEvent event)函数中,其通过调用setNdefPushMessageCallback (this, this) 后自动激活,其中,可以创建自己需要的NDEF消息并将其返回。 第67行为通过调用BobNdefMessage.getNdefMsg_from_RTD_URI(String uriFiledStr, byte identifierCode, boolean flagAddAAR)方法生成RTD-URI类型的NDEF消息,也可以创建其他类型的NDEF消息。关于BobNdefMessage类可参考第5章的5.3.2节。其中,此处flagAddAAR参数为true,表示增加AAR。 第31行为BNM步骤4阶段,即调用setOnNdefPushCompleteCallback(this, this)。实现该接口后,当成功将消息Beam to其他设备后,该Activity中会接收一个回调,onNdefPushComplete (NfcEvent event)函数会自动激活。 第79~82行为onNdefPushComplete(NfcEvent event)回调函数。在该函数里,以消息发送的机制处理Beam数据推送成功后的操作。 第72~75行为RBM步骤1阶段,该函数与Android NFC P2P实例1中的onResume()函数中相同,具体可参考对应内容。 第77~82行为RBM步骤2阶段,该函数与Android NFC P2P实例1中的onNewIntent(Intent intent)函数中相同,具体可参考对应内容。 第89~95行为RBM步骤3阶段,该函数与Android NFC P2P实例1中的resolveIntent(Intent intent)函数相同,具体可参考对应内容。 其他函数与Android NFC P2P实例1中对应的函数相同,此处不再赘述,具体可参考实例1中对应的内容。 第163~175行为通过Handler处理接收到的来自onNdef PushComplete(NfcEvent event)发送的消息,此处仅仅是Toast提示(如图6.12所示),读者可以在这里加入自己需要的功能。 1.setOnNdefPushCompleteCallback方法的原型 public void setOnNdefPushCompleteCallback (NfcAdapter.OnNdef PushCompleteCallback callback, Activity activity, Activity... activities):其中,callback为回调接口;activity为当前调用该方法的activity,即NDEF Push的Activity; activities为附加activity选项,强烈建议该方法在每个activity中只注册一次。 <div style="text-align: center"> <img src="https://yqfile.alicdn.com/ff5968336321784c12e4cb5a35d742eb1a8b698a.png" > </div> 使用setOnNdefPushCompleteCallback()方法时注意以下几点: (1)该方法可以在Activity中任何位置调用(onDestroy()之前)。因为callback只有Activity在前台状态(resume()状态)下才发生,所以Android的官方建议在OnCreate()中调用该方法,同时该方法并不阻塞线程,因此可以在UI主线程中使用。 (2)使用该方法时,如果callback为null,则调用该方法的Activity setOnNdefPushComplete Callback功能将会disable。 (3)关于该方法的使用,官方提供的使用范例如下(关于更详细的使用方法,读者可以参考实例4中的具体代码)。

    protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState); NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); if (nfcAdapter == null) return; // NFC not available on this device nfcAdapter.setOnNdefPushCompleteCallback(callback, this);

    }

    (4)使用该方法需要在AndroidManifest.xml中添加NFC权限。 (5)使用该方法需要在Android API 14+以上的系统中进行。 2.OnNdefPushCompleteCallback方法的原型 abstract void onNdefPushComplete(NfcEvent event):其中,event为通过Android Beam发送一个或多个动态生成的Uri(s)的回调接口;activity为当前调用该方法的activity,即push Uri(s)的Activity。 使用onNdefPushComplete()方法时应注意以下几点: (1)使用该方法需要在AndroidManifest.xml中添加NFC权限。 (2)使用该方法需要在Android API 14+以上的系统中进行。 相关资源:NFC点对点传输AndroidBeamDemo
    最新回复(0)