本节书摘来自异步社区《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;
- @Override
- protected void onCreate(Bundle savedInstanceState)
- {
- // TODO Auto-generated method stub
- super.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 stub
- mTextView = (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 stub
- mButton.setOnClickListener(new OnClickListener()
- {
- @Override
- public void onClick(View v)
- {
- // TODO Auto-generated method stub
- // BNM步骤1:准备NDEF数据
- // get the input message your want to push
- getInputMessage();
- 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 Push
- mNdefMessage = BobNdefMessage.getNdefMsg_from_RTD_TEXT(input
Text,false,false); - // BNM步骤2:enableForegroundNdefPush
- if (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 stub
- inputText = mEditText.getText().toString();
- }
- @Override
- protected void onResume()
- {
- // TODO Auto-generated method stub
- super.onResume();
- // RBM步骤2:消息判别
- if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction()))
- {
- resolveIntent(getIntent());
- } else
- {
- // BNM步骤4:enableForegroundNdefPush
- mNfcAdapter.enableForegroundNdefPush(this, BobNdefMessage.getNdef
Msg_from_RTD_TEXT("",false,false)); - }
- }
- @Override
- protected void onPause()
- {
- // TODO Auto-generated method stub
- super.onPause();
- // BNM步骤3:disableForegroundNdefPush
- if (null != mNfcAdapter)
- mNfcAdapter.disableForegroundNdefPush(this);
- }
- // RBM步骤1:onNewIntent setIntent(intent);
- @Override
- protected 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
- // String
- LogUtil.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");// 2 - String languageCode = new String(record.getPayload(), 1, languageCode
Length, Charset.forName("UTF-8")); -
LogUtil.i(MyConstant.Tag, Tag_ASSIST + "Language Code:" + languageCode
- "n"); // en
- int 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");// 1 - LogUtil.i(MyConstant.Tag, Tag_ASSIST + "the Record type: " + new String
(record.getType()) + "n");// T - LogUtil.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;
- @Override
- protected void onCreate(Bundle savedInstanceState)
- {
- // TODO Auto-generated method stub
- super.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 want
- mNfcAdapter.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)
- @Override
- public void onNdefPushComplete(NfcEvent event)
- {
- // TODO Auto-generated method stub
- LogUtil.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为例。
- @Override
- public NdefMessage createNdefMessage(NfcEvent event)
- {
- // TODO Auto-generated method stub
- LogUtil.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设置为true - return message;
- }
- @Override
- protected void onResume()
- {
- // Android NFC P2P实例1中的onResume()函数,该处省略
- }
- // RBM步骤1:onNewIntent setIntent(intent);
- @Override
- protected 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()
- {
- @Override
- public 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+以上的系统中进行。