nfc近场通信

NFC简介:

Near Field Communication 近场通信,是一种数据传输技术。

与wifi、蓝牙、红外线等数据传输技术的一个主要差异就是有效距离一般不能超过4cm。

NFC支持3种工作模式:

1.读卡器模式;

2.仿真卡模式;

3.点对点模式;

1.读卡器模式:

通过NFC设备(支持NFC的Android手机)从带有NFC芯片的标签、贴纸、报纸、明信片等媒介读取信息,或将数据写到这些媒介中。

2.仿真卡模式:

是将支持NFC的手机或其他电子设备当成借记卡、信用卡、公交卡、门禁卡等IC卡使用;基本原理是将相应的IC卡中的信息(支付凭证)封装成数据包存储在支持NFC的手机中,在使用时还需要一个NFC射频器(相当于刷传统IC卡时使用的刷卡器),将手机靠近NFC射频器,手机就会收到NFC射频器发过来的信号,在通过一系列复杂的验证后,将IC卡的相应信息传入NFC射频器,最后这些IC卡数据会传入NFC射频器连接的计算机,并进行相应的处理(如电子转账、开门等操作)。

3.点对点模式:

与蓝牙、红外差不多,可以用于不同的NFC设备之间进行数据交换,只是NFC的点对点模式有效距离更短,不能超过4cm;但是如果两个设备使用的都是Android4.2及以上版本,NFC会直接利用蓝牙传输,这种技术被称为Android Beam,所以Android Beam传输数据的两部设备不局限于4cm之内。

基础知识:

1.Android SDK API主要支持NFC论坛标准(Forum Standard),这种标准被称为NDEF(NFC Data Exchange Format,NFC数据交换格式);

2.Android SDK API支持如下三种NDEF数据的操作:

a.从NFC标签读取NDEF格式的数据;

b.向NFC标签写入NDEF格式的数据;

c.通过Android Beam技术将NDEF数据发送到另一部NFC设备;

3.在一个NFC设备读取NFC标签或另一个NFC设备中的数据之前会在0.1秒的时间之内建立NFC连接,然后数据会自动从被读取一端流向读取数据的一端;数据接收端会根据具体的数据格式和标签类型调用相应的Activity(这种行为也称为Tag Dispatch),这些Activity都需要定义Intent Filter,这些Intent Filter中就会指定不同的过滤机制,分为三个级别,也称为NFC的三重过滤机制。

4.NDEF_DISCOVERED:

只过滤固定格式的NDEF数据。例如:纯文本、指定协议(http、ftp、smb等)的URI等;

TECH_DISCOVERED:

当ACTION_NDEF_DISCOVERED指定的过滤机制无法匹配Tag时,就会使用这种过滤机制进行匹配,这种过滤机制并不是通过Tag中的数据格式进行匹配的,而是根据Tag支持的数据存储格式进行匹配,因此这种过滤机制的范围更广;

TAG_DISCOVERED:

如果将NFC过滤机制看成if...else if...else语句的话,那么这种过滤机制就相当于else部分,当前面两种过滤机制都匹配失败后,系统就会利用这种过滤机制来处理,这种过滤机制用来处理未识别的Tag(数据格式不对,而且Tag支持的格式也不匹配)。

5.Android系统会依次匹配NDEF_DISCOVERED、TECH_DISCOVERED和TAG_DISCOVERED;如果通过三重过滤机制仍然无法匹配Tag,则什么都不做;通常在成功匹配Tag后,Android设备会发出比较清脆的声音,而未成功匹配Tag,就会发出比较沉闷的声音。

此过程的处理流程如下图所示:

nfc近场通信

6.在manifest文件中需要设置的部分有:

设置权限:

<uses-permission android:name="android.permission.NFC" />

限制Android版本:

android:minSdkVersion="14"

限制安装的设备:

<uses-feature  android:name="android.hardware.nfc"  android:required="true" />

设置Activity的Intent Filter,比如设置为三种过滤机制的一种:

<intent-filter>

    <action android:name="android.nfc.action.TECH_DISCOVERED" />

</intent-filter>

接下来,我们来第一个例子,这个例子是属于读卡器模式,从NFC芯片中读取和写入数据。

它的manifest文件内容如下:

[html] view
plain
copy
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.r8c.nfc_demo"
  4. android:versionCode="110"
  5. android:versionName="1.1.0" >
  6. <uses-sdk
  7. android:minSdkVersion="15"
  8. android:targetSdkVersion="17" />
  9. <!-- NFC权限声明 -->
  10. <uses-permission android:name="android.permission.NFC" />
  11. <uses-feature
  12. android:name="android.hardware.nfc"
  13. android:required="true" />
  14. <application
  15. android:allowBackup="true"
  16. android:icon="@drawable/ic_launcher"
  17. android:label="@string/app_name"
  18. android:theme="@style/AppTheme" >
  19. <activity
  20. android:name="com.r8c.nfc_demo.NfcDemoActivity"
  21. android:configChanges="orientation|keyboardHidden|screenSize"
  22. android:label="@string/app_name"
  23. android:launchMode="singleTask">
  24. <intent-filter>
  25. <action android:name="android.intent.action.MAIN" />
  26. <category android:name="android.intent.category.LAUNCHER" />
  27. </intent-filter>
  28. <!-- TECH_DISCOVERED类型的nfc -->
  29. <intent-filter>
  30. <action android:name="android.nfc.action.TECH_DISCOVERED" />
  31. </intent-filter>
  32. <!-- 后设资源  调用自己建立的文件夹xml中的文件 -->
  33. <meta-data
  34. android:name="android.nfc.action.TECH_DISCOVERED"
  35. android:resource="@xml/nfc_tech_filter" />
  36. </activity>
  37. </application>
  38. </manifest>

它的Activity的内容如下,包括读取、写入、删除三大功能:(其中删除功能是通过写入空值来实现的)

[java] view
plain
copy
  1. import java.io.IOException;
  2. import java.io.UnsupportedEncodingException;
  3. import java.nio.charset.Charset;
  4. import android.media.AudioManager;
  5. import android.media.MediaPlayer;
  6. import android.media.RingtoneManager;
  7. import android.net.Uri;
  8. import android.nfc.FormatException;
  9. import android.nfc.NdefMessage;
  10. import android.nfc.NdefRecord;
  11. import android.nfc.NfcAdapter;
  12. import android.nfc.Tag;
  13. import android.nfc.tech.MifareUltralight;
  14. import android.nfc.tech.Ndef;
  15. import android.nfc.tech.NfcA;
  16. import android.os.Bundle;
  17. import android.app.Activity;
  18. import android.app.PendingIntent;
  19. import android.content.Context;
  20. import android.content.Intent;
  21. import android.content.IntentFilter;
  22. import android.graphics.Color;
  23. import android.util.Log;
  24. import android.view.Menu;
  25. import android.view.View;
  26. import android.view.View.OnClickListener;
  27. import android.widget.Button;
  28. import android.widget.TextView;
  29. import android.widget.Toast;
  30. public class NfcDemoActivity extends Activity implements OnClickListener {
  31. // NFC适配器
  32. private NfcAdapter nfcAdapter = null;
  33. // 传达意图
  34. private PendingIntent pi = null;
  35. // 滤掉组件无法响应和处理的Intent
  36. private IntentFilter tagDetected = null;
  37. // 文本控件
  38. private TextView promt = null;
  39. // 是否支持NFC功能的标签
  40. private boolean isNFC_support = false;
  41. // 读、写、删按钮控件
  42. private Button readBtn, writeBtn, deleteBtn;
  43. @Override
  44. protected void onCreate(Bundle savedInstanceState) {
  45. super.onCreate(savedInstanceState);
  46. setContentView(R.layout.activity_nfc_demo);
  47. setupViews();
  48. initNFCData();
  49. }
  50. @Override
  51. protected void onResume() {
  52. super.onResume();
  53. if (isNFC_support == false) {
  54. // 如果设备不支持NFC或者NFC功能没开启,就return掉
  55. return;
  56. }
  57. // 开始监听NFC设备是否连接
  58. startNFC_Listener();
  59. if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(this.getIntent()
  60. .getAction())) {
  61. // 注意这个if中的代码几乎不会进来,因为刚刚在上一行代码开启了监听NFC连接,下一行代码马上就收到了NFC连接的intent,这种几率很小
  62. // 处理该intent
  63. processIntent(this.getIntent());
  64. }
  65. }
  66. @Override
  67. protected void onPause() {
  68. super.onPause();
  69. if (isNFC_support == true) {
  70. // 当前Activity如果不在手机的最前端,就停止NFC设备连接的监听
  71. stopNFC_Listener();
  72. }
  73. }
  74. @Override
  75. protected void onNewIntent(Intent intent) {
  76. super.onNewIntent(intent);
  77. // 当前app正在前端界面运行,这个时候有intent发送过来,那么系统就会调用onNewIntent回调方法,将intent传送过来
  78. // 我们只需要在这里检验这个intent是否是NFC相关的intent,如果是,就调用处理方法
  79. if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) {
  80. processIntent(intent);
  81. }
  82. }
  83. @Override
  84. public void onClick(View v) {
  85. // 点击读按钮后
  86. if (v.getId() == R.id.read_btn) {
  87. try {
  88. String content = read(tagFromIntent);
  89. if (content != null && !content.equals("")) {
  90. promt.setText(promt.getText() + "nfc标签内容:\n" + content
  91. + "\n");
  92. } else {
  93. promt.setText(promt.getText() + "nfc标签内容:\n" + "内容为空\n");
  94. }
  95. } catch (IOException e) {
  96. promt.setText(promt.getText() + "错误:" + e.getMessage() + "\n");
  97. Log.e("myonclick", "读取nfc异常", e);
  98. } catch (FormatException e) {
  99. promt.setText(promt.getText() + "错误:" + e.getMessage() + "\n");
  100. Log.e("myonclick", "读取nfc异常", e);
  101. }
  102. // 点击写后写入
  103. } else if (v.getId() == R.id.write_btn) {
  104. try {
  105. write(tagFromIntent);
  106. } catch (IOException e) {
  107. promt.setText(promt.getText() + "错误:" + e.getMessage() + "\n");
  108. Log.e("myonclick", "写nfc异常", e);
  109. } catch (FormatException e) {
  110. promt.setText(promt.getText() + "错误:" + e.getMessage() + "\n");
  111. Log.e("myonclick", "写nfc异常", e);
  112. }
  113. } else if (v.getId() == R.id.delete_btn) {
  114. try {
  115. delete(tagFromIntent);
  116. } catch (IOException e) {
  117. promt.setText(promt.getText() + "错误:" + e.getMessage() + "\n");
  118. Log.e("myonclick", "删除nfc异常", e);
  119. } catch (FormatException e) {
  120. promt.setText(promt.getText() + "错误:" + e.getMessage() + "\n");
  121. Log.e("myonclick", "删除nfc异常", e);
  122. }
  123. }
  124. }
  125. private void setupViews() {
  126. // 控件的绑定
  127. promt = (TextView) findViewById(R.id.promt);
  128. readBtn = (Button) findViewById(R.id.read_btn);
  129. writeBtn = (Button) findViewById(R.id.write_btn);
  130. deleteBtn = (Button) findViewById(R.id.delete_btn);
  131. // 给文本控件赋值初始文本
  132. promt.setText("等待RFID标签");
  133. // 监听读、写、删按钮控件
  134. readBtn.setOnClickListener(this);
  135. writeBtn.setOnClickListener(this);
  136. deleteBtn.setOnClickListener(this);
  137. }
  138. private void initNFCData() {
  139. // 初始化设备支持NFC功能
  140. isNFC_support = true;
  141. // 得到默认nfc适配器
  142. nfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
  143. // 提示信息定义
  144. String metaInfo = "";
  145. // 判定设备是否支持NFC或启动NFC
  146. if (nfcAdapter == null) {
  147. metaInfo = "设备不支持NFC!";
  148. Toast.makeText(this, metaInfo, Toast.LENGTH_SHORT).show();
  149. isNFC_support = false;
  150. }
  151. if (!nfcAdapter.isEnabled()) {
  152. metaInfo = "请在系统设置中先启用NFC功能!";
  153. Toast.makeText(this, metaInfo, Toast.LENGTH_SHORT).show();
  154. isNFC_support = false;
  155. }
  156. if (isNFC_support == true) {
  157. init_NFC();
  158. } else {
  159. promt.setTextColor(Color.RED);
  160. promt.setText(metaInfo);
  161. }
  162. }
  163. @Override
  164. public boolean onCreateOptionsMenu(Menu menu) {
  165. // Inflate the menu; this adds items to the action bar if it is present.
  166. getMenuInflater().inflate(R.menu.nfc_demo, menu);
  167. return true;
  168. }
  169. // 字符序列转换为16进制字符串
  170. private String bytesToHexString(byte[] src) {
  171. return bytesToHexString(src, true);
  172. }
  173. private String bytesToHexString(byte[] src, boolean isPrefix) {
  174. StringBuilder stringBuilder = new StringBuilder();
  175. if (isPrefix == true) {
  176. stringBuilder.append("0x");
  177. }
  178. if (src == null || src.length <= 0) {
  179. return null;
  180. }
  181. char[] buffer = new char[2];
  182. for (int i = 0; i < src.length; i++) {
  183. buffer[0] = Character.toUpperCase(Character.forDigit(
  184. (src[i] >>> 4) & 0x0F, 16));
  185. buffer[1] = Character.toUpperCase(Character.forDigit(src[i] & 0x0F,
  186. 16));
  187. System.out.println(buffer);
  188. stringBuilder.append(buffer);
  189. }
  190. return stringBuilder.toString();
  191. }
  192. private Tag tagFromIntent;
  193. /**
  194. * Parses the NDEF Message from the intent and prints to the TextView
  195. */
  196. public void processIntent(Intent intent) {
  197. if (isNFC_support == false)
  198. return;
  199. // 取出封装在intent中的TAG
  200. tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
  201. promt.setTextColor(Color.BLUE);
  202. String metaInfo = "";
  203. metaInfo += "卡片ID:" + bytesToHexString(tagFromIntent.getId()) + "\n";
  204. Toast.makeText(this, "找到卡片", Toast.LENGTH_SHORT).show();
  205. // Tech List
  206. String prefix = "android.nfc.tech.";
  207. String[] techList = tagFromIntent.getTechList();
  208. //分析NFC卡的类型: Mifare Classic/UltraLight Info
  209. String CardType = "";
  210. for (int i = 0; i < techList.length; i++) {
  211. if (techList[i].equals(NfcA.class.getName())) {
  212. // 读取TAG
  213. NfcA mfc = NfcA.get(tagFromIntent);
  214. try {
  215. if ("".equals(CardType))
  216. CardType = "MifareClassic卡片类型 \n 不支持NDEF消息 \n";
  217. } catch (Exception e) {
  218. e.printStackTrace();
  219. }
  220. } else if (techList[i].equals(MifareUltralight.class.getName())) {
  221. MifareUltralight mifareUlTag = MifareUltralight
  222. .get(tagFromIntent);
  223. String lightType = "";
  224. // Type Info
  225. switch (mifareUlTag.getType()) {
  226. case MifareUltralight.TYPE_ULTRALIGHT:
  227. lightType = "Ultralight";
  228. break;
  229. case MifareUltralight.TYPE_ULTRALIGHT_C:
  230. lightType = "Ultralight C";
  231. break;
  232. }
  233. CardType = lightType + "卡片类型\n";
  234. Ndef ndef = Ndef.get(tagFromIntent);
  235. CardType += "最大数据尺寸:" + ndef.getMaxSize() + "\n";
  236. }
  237. }
  238. metaInfo += CardType;
  239. promt.setText(metaInfo);
  240. }
  241. // 读取方法
  242. private String read(Tag tag) throws IOException, FormatException {
  243. if (tag != null) {
  244. //解析Tag获取到NDEF实例
  245. Ndef ndef = Ndef.get(tag);
  246. //打开连接
  247. ndef.connect();
  248. //获取NDEF消息
  249. NdefMessage message = ndef.getNdefMessage();
  250. //将消息转换成字节数组
  251. byte[] data = message.toByteArray();
  252. //将字节数组转换成字符串
  253. String str = new String(data, Charset.forName("UTF-8"));
  254. //关闭连接
  255. ndef.close();
  256. return str;
  257. } else {
  258. Toast.makeText(NfcDemoActivity.this, "设备与nfc卡连接断开,请重新连接...",
  259. Toast.LENGTH_SHORT).show();
  260. }
  261. return null;
  262. }
  263. // 写入方法
  264. private void write(Tag tag) throws IOException, FormatException {
  265. if (tag != null) {
  266. //新建NdefRecord数组,本例中数组只有一个元素
  267. NdefRecord[] records = { createRecord() };
  268. //新建一个NdefMessage实例
  269. NdefMessage message = new NdefMessage(records);
  270. // 解析TAG获取到NDEF实例
  271. Ndef ndef = Ndef.get(tag);
  272. // 打开连接
  273. ndef.connect();
  274. // 写入NDEF信息
  275. ndef.writeNdefMessage(message);
  276. // 关闭连接
  277. ndef.close();
  278. promt.setText(promt.getText() + "写入数据成功!" + "\n");
  279. } else {
  280. Toast.makeText(NfcDemoActivity.this, "设备与nfc卡连接断开,请重新连接...",
  281. Toast.LENGTH_SHORT).show();
  282. }
  283. }
  284. // 删除方法
  285. private void delete(Tag tag) throws IOException, FormatException {
  286. if (tag != null) {
  287. //新建一个里面无任何信息的NdefRecord实例
  288. NdefRecord nullNdefRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA,
  289. new byte[] {}, new byte[] {}, new byte[] {});
  290. NdefRecord[] records = { nullNdefRecord };
  291. NdefMessage message = new NdefMessage(records);
  292. // 解析TAG获取到NDEF实例
  293. Ndef ndef = Ndef.get(tag);
  294. // 打开连接
  295. ndef.connect();
  296. // 写入信息
  297. ndef.writeNdefMessage(message);
  298. // 关闭连接
  299. ndef.close();
  300. promt.setText(promt.getText() + "删除数据成功!" + "\n");
  301. } else {
  302. Toast.makeText(NfcDemoActivity.this, "设备与nfc卡连接断开,请重新连接...",
  303. Toast.LENGTH_SHORT).show();
  304. }
  305. }
  306. //返回一个NdefRecord实例
  307. private NdefRecord createRecord() throws UnsupportedEncodingException {
  308. //组装字符串,准备好你要写入的信息
  309. String msg = "BEGIN:VCARD\n" + "VERSION:2.1\n" + "中国湖北省武汉市\n"
  310. + "武汉大学计算机学院\n" + "END:VCARD";
  311. //将字符串转换成字节数组
  312. byte[] textBytes = msg.getBytes();
  313. //将字节数组封装到一个NdefRecord实例中去
  314. NdefRecord textRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA,
  315. "text/x-vCard".getBytes(), new byte[] {}, textBytes);
  316. return textRecord;
  317. }
  318. private MediaPlayer ring() throws Exception, IOException {
  319. // TODO Auto-generated method stub
  320. Uri alert = RingtoneManager
  321. .getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
  322. MediaPlayer player = new MediaPlayer();
  323. player.setDataSource(this, alert);
  324. final AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
  325. if (audioManager.getStreamVolume(AudioManager.STREAM_NOTIFICATION) != 0) {
  326. player.setAudioStreamType(AudioManager.STREAM_NOTIFICATION);
  327. player.setLooping(false);
  328. player.prepare();
  329. player.start();
  330. }
  331. return player;
  332. }
  333. private void startNFC_Listener() {
  334. // 开始监听NFC设备是否连接,如果连接就发pi意图
  335. nfcAdapter.enableForegroundDispatch(this, pi,
  336. new IntentFilter[] { tagDetected }, null);
  337. }
  338. private void stopNFC_Listener() {
  339. // 停止监听NFC设备是否连接
  340. nfcAdapter.disableForegroundDispatch(this);
  341. }
  342. private void init_NFC() {
  343. // 初始化PendingIntent,当有NFC设备连接上的时候,就交给当前Activity处理
  344. pi = PendingIntent.getActivity(this, 0, new Intent(this, getClass())
  345. .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
  346. // 新建IntentFilter,使用的是第二种的过滤机制
  347. tagDetected = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
  348. tagDetected.addCategory(Intent.CATEGORY_DEFAULT);
  349. }
  350. }

下面是该示例的完整源码下载链接:

Android NFC Demo1

上一篇:(转)YUV420、YUV422、RGB24转换


下一篇:Unieap3.5-前台js用SQL语句执行数据请求