本文共 45964 字,大约阅读时间需要 153 分钟。
本节书摘来自异步社区《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 CreateNdefMessageCallback5. { // 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. @Override13. protected void onCreate(Bundle savedInstanceState)14. {15. // TODO Auto-generated method stub16. 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 want26. mNfcAdapter.setNdefPushMessageCallback(this, this);27. }28. 29. /*30. * (non-Javadoc)31. * 32. * @see33. * android.nfc.NfcAdapter.CreateNdefMessageCallback#createNdefMessage (android34. * .nfc.NfcEvent)35. */36. // BNM步骤3:回调函数中实现Beam Data。37. // 当发现有支持Beam的手机时,该回调接口会自动激活,你只需将你需要Beam的NDEF消息准备好 //并作为NdefMessage返回即可。本例中以RTD_URI为例。38. @Override39. public NdefMessage createNdefMessage(NfcEvent event)40. {41. // TODO Auto-generated method stub42. 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. @Override50. protected void onResume()51. {52. // TODO Auto-generated method stub53. 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. @Override65. protected void onNewIntent(Intent intent)66. {67. // TODO Auto-generated method stub68. // super.onNewIntent(intent);69. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "into onNewIntent");70. setIntent(intent);71. }72. 73. // RBM步骤3:处理接收的数据74. /**75. * @param intent76. */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. } else95. {96. // Unknown tag type97. 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 views106. setTitle(R.string.title_scanned_tag);107. // process NDEF Msg108. 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. } else116. {117. LogUtil.e(MyConstant.Tag, Tag_ASSIST + "Unknown intent " + intent);118. finish();119. return;120. }121. }122. 123. /**124. * 获取NdefMessage125. * @param messages126. */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. * 解析NdefMessage155. * @param record156. */157. private void parseUriRecord(NdefRecord record)158. {159. // TODO Auto-generated method stub160. 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. } else169. {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 stub177. 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");// 1181. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record type: " + new String(record.getType()) + "\n");// T182. 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 record189. * 190. * payload[0] contains the URI Identifier Code, per the NFC Forum191. * "URI Record Type Definition" section 3.2.2.192. * 193. * payload[1]...payload[payload.length - 1] contains the rest of194. * the URI.195. */196. private void parseWellKnownUriRecord(NdefRecord record)197. {198. // TODO Auto-generated method stub199. 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 uri219. */220. private void uiControl(final Uri uri)221. {222. // TODO Auto-generated method stub223. 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. try237. {238. startActivity(data);239. } catch (ActivityNotFoundException e)240. {241. return;242. }243. }244. });245. }246. 247. /**248. * @param record249. * @return250. */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. } else259. {260. return false;261. }262. } else if (record.getTnf() == NdefRecord.TNF_ABSOLUTE_URI)263. {264. return true;265. } else266. {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, per275. * section 3.2.2 of the NFC Forum URI Record Type Definition document.276. */277. private static final BiMapURI_PREFIX_MAP = ImmutableBiMap. 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-2293. */294. private void checkNFCFunction()295. {296. // TODO Auto-generated method stub297. 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);// back322. dialog.setCanceledOnTouchOutside(false);323. SetDialogWidth(dialog).show();324. return;325. } else326. {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 = new347. // Intent(Settings.ACTION_NFC_SETTINGS);348. startActivity(setnfc);349. }350. });351. dialog = customBuilder.create();352. dialog.setCancelable(false);// back353. 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 dialog367. * @return368. */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. } else384. {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. 2.7. 8. 15. 16. 23.
第8~14行为按钮控件,此处的按钮控件只是起提示作用。
第16~23行为文本控件,文本控件主要显示需要Beam的数据以及接收到的Beam数据。在AndroidManifest.xml中声明Activity,并添加NFC权限,其代码如下:1.2. 5. 6. 9. 10.7. 8. 11. 18.12. 13. 17.
第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 Activity5. {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. @Override17. protected void onCreate(Bundle savedInstanceState)18. {19. // TODO Auto-generated method stub20. 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 stub32. 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 stub40. mButton.setOnClickListener(new OnClickListener()41. {42. @Override43. public void onClick(View v)44. {45. // TODO Auto-generated method stub46. // BNM步骤1: Create NDEF Msg47. // get the input message your want to push48. 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 Push55. NdefMessage message = BobNdefMessage.getNdefMsg_from_RTD_ TEXT(inputText, false, false);56. 57. // BNM步骤2:call setNdefPushMessage anywhere your want58. 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 stub71. inputText = mEditText.getText().toString();72. }73. 74. @Override75. protected void onResume()76. {77. // TODO Auto-generated method stub78. 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. @Override90. protected void onNewIntent(Intent intent)91. {92. // TODO Auto-generated method stub93. // super.onNewIntent(intent);94. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "into onNewIntent");95. setIntent(intent);96. }97. 98. /**99. * RBM步骤3:处理接收的数据* 100. * @param intent101. */102. void resolveIntent(Intent intent)103. {104. // Android NFC P2P实例1中的resolveIntent(Intent intent)函数,该处省略105. }106. 107. /**108. * 获取NdefMessage109. * 110. * @param messages111. */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 record140. * @return141. */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. } else150. {151. return false;152. }153. } else154. {155. return false;156. }157. }158. 159. 160. /**161. * @param record162. * payload[0] contains the "Status Byte Encodings" field, per the163. * 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 UTF16169. * 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. try194. {195. payloadStr = new String(payload, languageCodeLength + 1, payload. length - languageCodeLength - 1, textEncoding); // 获取payload实际数据196. 197. } catch (UnsupportedEncodingException e)198. {199. // TODO Auto-generated catch block200. e.printStackTrace();// 异常信息201. }202. 203. LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record Tnf: " + record.getTnf()+ "\n");// 1204. // ,状态字节编码信息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-2213. */214. private void checkNFCFunction()215. {216. // Android NFC P2P实例1中的checkNFCFunction ()函数,该处省略217. }218. 219. /**220. * @param dialog221. * @return222. */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. 2.6. 7. 13. 14. 22. 23. 24. 25. 32. 33.
第7~12行为文本控件,主要显示需要Beam的数据以及接收到的Beam数据。
第14~23行为编辑框控件,作为Beam to Share文本的输入。第25~31行为按钮控件,按住开始Beam数据。AndroidManifest.xml中声明Activity,并添加NFC权限,其代码如下:``第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文件代码如下:
``LogUtil.i(MyConstant.Tag, Tag_ASSIST + "Language Code:" + languageCode
LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record payload: " + payload
第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中包含了一个按钮控件、一个输入编辑框控件和一个文本控件,并修改了相关属性,代码如下:
``第13~21行为编辑框控件,作为Foreground NDEF Push文本的输入。
第23~30行为按钮控件,按住开始Push NDEF数据。AndroidManifest.xml中声明Activity,并添加NFC权限,其代码如下:第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所示。**6.3.4 实例4:结合AAR实现Android Beam**在第4章中详细介绍了AAR相关知识。Android NFC P2P实例4 中结合了AAR和 setNdefPush MessageCallback(Android NFC P2P实例1)来实现Android Beam,同时还增加了对BNM后的结果进行了处理。主要代码与实例1相似,详细代码如下:
第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中只注册一次。使用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+以上的系统中进行。
转载地址:http://ehzeo.baihongyu.com/