很多Android设备已经支持NFC(近距离无线通讯技术)了。本文就以实例的方式,为大家介绍如何在Android系统中进行NFC开发。

Android NFC开发环境

使用硬件:Google Nexus S,北京大学学生卡。(ps:笔者本想使用公交一卡通进行测试,发现手机不能正确识别)

手机操作系统:Android ICS 4.04。

开发时,笔者从Google Play Store上下载了NFC TagInfo软件进行对比学习。所以我们可以使用任意一张能被TagInfo软件正确识别的卡做测试。

在Android NFC 应用中,Android手机通常是作为通信中的发起者,也就是作为各种NFC卡的读写器。Android对NFC的支持主要在 android.nfc 和android.nfc.tech 两个包中。

android.nfc 包中主要类如下:

NfcManager 可以用来管理Android设备中指出的所有NFCAdapter,但由于大部分Android设备只支持一个NFC Adapter,所以一般直接调用getDefaultAapater来获取手机中的Adapter。

NfcAdapter 相当于一个NFC适配器,类似于电脑装了网络适配器才能上网,手机装了NfcAdapter才能发起NFC通信。

NDEF: NFC Data Exchange Format,即NFC数据交换格式。

NdefMessage 和NdefRecord NDEF 为NFC forum 定义的数据格式。

Tag 代表一个被动式Tag对象,可以代表一个标签,卡片等。当Android设备检测到一个Tag时,会创建一个Tag对象,将其放在Intent对象,然后发送到相应的Activity。

android.nfc.tech 中则定义了可以对Tag进行的读写操作的类,这些类按照其使用的技术类型可以分成不同的类如:NfcA, NfcB, NfcF,以及MifareClassic 等。其中MifareClassic比较常见。

在本次实例中,笔者使用北京大学学生卡进行数据读取测试,学生卡的TAG类型为MifareClassic。

NFC开发实例讲解

AndroidManifest.xml

XML/HTML代码
  1. <span style="font-size:16px;"><?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="org.reno"
  4. android:versionCode="1"
  5. android:versionName="1.0" >
  6. <uses-permission android:name="android.permission.NFC" />
  7. <uses-sdk android:minSdkVersion="14" />
  8. <uses-feature android:name="android.hardware.nfc" android:required="true" />
  9. <application
  10. android:icon="@drawable/ic_launcher"
  11. android:label="@string/app_name" >
  12. <activity
  13. android:name="org.reno.Beam"
  14. android:label="@string/app_name"
  15. android:launchMode="singleTop" >
  16. <intent-filter>
  17. <action android:name="android.intent.action.MAIN" />
  18. <category android:name="android.intent.category.LAUNCHER" />
  19. </intent-filter>
  20. <intent-filter>
  21. <action android:name="android.nfc.action.TECH_DISCOVERED" />
  22. </intent-filter>
  23. <meta-data
  24. android:name="android.nfc.action.TECH_DISCOVERED"
  25. android:resource="@xml/nfc_tech_filter" />
  26. </activity>
  27. </application>
  28. </manifest>
  29. </span>

res/xml/nfc_tech_filter.xml:

XML/HTML代码
  1. <resourcesxmlns:xliffresourcesxmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
  2. <tech-list>
  3. <tech>android.nfc.tech.MifareClassic</tech>
  4. </tech-list>
  5. </resources>
  6. <uses-permission android:name="android.permission.NFC"/>
  7. <uses-feature android:name="android.hardware.nfc" android:required="true"/>

表示会使用到硬件的NFC功能。并且当用户在Google Play Store中搜索时,只有带有NFC功能的手机才能够搜索到本应用。

当手机开启了NFC,并且检测到一个TAG后,TAG分发系统会自动创建一个封装了NFC TAG信息的intent。如果多于一个应用程序能够处理这个intent的话,那么手机就会弹出一个框,让用户选择处理该TAG的Activity。TAG分发系统定义了3中intent。按优先级从高到低排列为:

NDEF_DISCOVERED, TECH_DISCOVERED, TAG_DISCOVERED

当Android设备检测到有NFC Tag靠近时,会根据Action申明的顺序给对应的Activity 发送含NFC消息的 Intent。

此处我们使用的intent-filter的Action类型为TECH_DISCOVERED从而可以处理所有类型为ACTION_TECH_DISCOVERED并且使用的技术为nfc_tech_filter.xml文件中定义的类型的TAG。

下图为当手机检测到一个TAG时,启用Activity的匹配过程。

res/layout/main.xml:

XML/HTML代码
  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. <ScrollView
  7. android:id="@+id/scrollView"
  8. android:layout_width="fill_parent"
  9. android:layout_height="fill_parent"
  10. android:background="@android:drawable/edit_text" >
  11. <TextView
  12. android:id="@+id/promt"
  13. android:layout_width="fill_parent"
  14. android:layout_height="wrap_content"
  15. android:scrollbars="vertical"
  16. android:singleLine="false"
  17. android:text="@string/info" />
  18. </ScrollView>
  19. </LinearLayout>

定义了Activity的布局:只有一个带有滚动条的TextView用于显示从TAG中读取的信息。

res/values/strings.xml:

XML/HTML代码
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3. <string name="app_name">NFC测试</string>
  4. <string name="info">扫描中。。。</string>
  5. </resources>

src/org/reno/Beam.java:

Java代码
  1. package org.reno;
  2. import android.app.Activity;
  3. import android.content.Intent;
  4. import android.nfc.NfcAdapter;
  5. import android.nfc.Tag;
  6. import android.nfc.tech.MifareClassic;
  7. import android.os.Bundle;
  8. import android.widget.TextView;
  9. public class Beam extends Activity {
  10. NfcAdapter nfcAdapter;
  11. TextView promt;
  12. @Override
  13. public void onCreate(Bundle savedInstanceState) {
  14. super.onCreate(savedInstanceState);
  15. setContentView(R.layout.main);
  16. promt = (TextView) findViewById(R.id.promt);
  17. // 获取默认的NFC控制器
  18. nfcAdapter = NfcAdapter.getDefaultAdapter(this);
  19. if (nfcAdapter == null) {
  20. promt.setText("设备不支持NFC!");
  21. finish();
  22. return;
  23. }
  24. if (!nfcAdapter.isEnabled()) {
  25. promt.setText("请在系统设置中先启用NFC功能!");
  26. finish();
  27. return;
  28. }
  29. }
  30. @Override
  31. protected void onResume() {
  32. super.onResume();
  33. //得到是否检测到ACTION_TECH_DISCOVERED触发
  34. if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(getIntent().getAction())) {
  35. //处理该intent
  36. processIntent(getIntent());
  37. }
  38. }
  39. //字符序列转换为16进制字符串
  40. private String bytesToHexString(byte[] src) {
  41. StringBuilder stringBuilder = new StringBuilder("0x");
  42. if (src == null || src.length <= 0) {
  43. return null;
  44. }
  45. char[] buffer = new char[2];
  46. for (int i = 0; i < src.length; i++) {
  47. buffer[0] = Character.forDigit((src[i] >>> 4) & 0x0F, 16);
  48. buffer[1] = Character.forDigit(src[i] & 0x0F, 16);
  49. System.out.println(buffer);
  50. stringBuilder.append(buffer);
  51. }
  52. return stringBuilder.toString();
  53. }
  54. /**
  55. * Parses the NDEF Message from the intent and prints to the TextView
  56. */
  57. private void processIntent(Intent intent) {
  58. //取出封装在intent中的TAG
  59. Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
  60. for (String tech : tagFromIntent.getTechList()) {
  61. System.out.println(tech);
  62. }
  63. boolean auth = false;
  64. //读取TAG
  65. MifareClassic mfc = MifareClassic.get(tagFromIntent);
  66. try {
  67. String metaInfo = "";
  68. //Enable I/O operations to the tag from this TagTechnology object.
  69. mfc.connect();
  70. int type = mfc.getType();//获取TAG的类型
  71. int sectorCount = mfc.getSectorCount();//获取TAG中包含的扇区数
  72. String typeS = "";
  73. switch (type) {
  74. case MifareClassic.TYPE_CLASSIC:
  75. typeS = "TYPE_CLASSIC";
  76. break;
  77. case MifareClassic.TYPE_PLUS:
  78. typeS = "TYPE_PLUS";
  79. break;
  80. case MifareClassic.TYPE_PRO:
  81. typeS = "TYPE_PRO";
  82. break;
  83. case MifareClassic.TYPE_UNKNOWN:
  84. typeS = "TYPE_UNKNOWN";
  85. break;
  86. }
  87. metaInfo += "卡片类型:" + typeS + "\n共" + sectorCount + "个扇区\n共"
  88. + mfc.getBlockCount() + "个块\n存储空间: " + mfc.getSize() + "B\n";
  89. for (int j = 0; j < sectorCount; j++) {
  90. //Authenticate a sector with key A.
  91. auth = mfc.authenticateSectorWithKeyA(j,
  92. MifareClassic.KEY_DEFAULT);
  93. int bCount;
  94. int bIndex;
  95. if (auth) {
  96. metaInfo += "Sector " + j + ":验证成功\n";
  97. // 读取扇区中的块
  98. bCount = mfc.getBlockCountInSector(j);
  99. bIndex = mfc.sectorToBlock(j);
  100. for (int i = 0; i < bCount; i++) {
  101. byte[] data = mfc.readBlock(bIndex);
  102. metaInfo += "Block " + bIndex + " : "
  103. + bytesToHexString(data) + "\n";
  104. bIndex++;
  105. }
  106. } else {
  107. metaInfo += "Sector " + j + ":验证失败\n";
  108. }
  109. }
  110. promt.setText(metaInfo);
  111. } catch (Exception e) {
  112. e.printStackTrace();
  113. }
  114. }
  115. }

关于MifareClassic卡的背景介绍:数据分为16个区(Sector) ,每个区有4个块(Block) ,每个块可以存放16字节的数据。

每个区最后一个块称为Trailer ,主要用来存放读写该区Block数据的Key ,可以有A,B两个Key,每个Key 长度为6个字节,缺省的Key值一般为全FF或是0。由MifareClassic.KEY_DEFAULT 定义。

因此读写Mifare Tag 首先需要有正确的Key值(起到保护的作用),如果鉴权成功,然后才可以读写该区数据。

执行效果:

nfc开发的更多相关文章

  1. 《Android NFC 开发实战详解 》简介+源码+样章+勘误ING

    <Android NFC 开发实战详解>简介+源码+样章+勘误ING SkySeraph Mar. 14th  2014 Email:skyseraph00@163.com 更多精彩请直接 ...

  2. Android NFC开发概述

    NFC手机相比普通手机来说,有以下3个附加功能:  1.可以当成POS机来用,也就是“读取”模式   2.可以当成一张卡来刷,也就是NFC技术最核心的移动支付功能  3.可以像蓝牙.Wi-Fi一样做点 ...

  3. Android NFC开发(二)——Android世界里的NFC所具备的条件以及使用方法

    Android NFC开发(二)--Android世界里的NFC所具备的条件以及使用方法 NFC的应用比较广泛,而且知识面也是比较广的,所以就多啰嗦了几句,我还还是得跟着官方文档:http://dev ...

  4. Android NFC开发(一)——初探NFC,了解当前前沿技术

    Android NFC开发(一)--初探NFC,了解当前前沿技术 官方文档:http://developer.android.com/guide/topics/connectivity/nfc/ind ...

  5. 《NFC开发实战详解》笔记

    地点:30教 5楼 男厕对面 * 时间:下午三点 * 天气:中雨 * 状态:3    * ******************************************************* ...

  6. 用Android模拟器也可以开发和测试NFC应用

    从Android2.3开始支持NFC.不过NFC应用只能在Android手机(或平板电脑)上测试和开发,而且Android手机还必须有NFC芯 片.而且如果测试NFC传输文件时至少需要两部支持NFC的 ...

  7. Eclipse的NDEF插件诞生,将加速NFC应用开发

    今年2月份,NFC论坛刚刚发布了NFC技术的首个规范NDEF(nfc data exchange format)-即NFC数据交换规范.而不到2个月的今天Eclipse就发布了基于NDEF规范的NFC ...

  8. Android NFC技术(三)——初次开发Android NFC你须知道NdefMessage和NdefRecord

    Android NFC技术(三)--初次开发Android NFC你须知道NdefMessage和NdefRecord 这最近也是有好多天没写博客了,除了到处张罗着搬家之外,依旧还是许许多多的琐事阻碍 ...

  9. android nfc功能开发

    链接:Android NFC开发详细总结   https://blog.csdn.net/zhwadezh/article/details/79111348 链接2:Android NFC功能 简单实 ...

随机推荐

  1. 3064: Tyvj 1518 CPU监控

    注意这题要维护历史最大加和历史最大覆盖 /************************************************************** Problem: 3064 Us ...

  2. VK Cup 2017 - Квалификация 1

    CF上的VK Cup 2017资格赛1,好像很水,因为只有俄文所以语言是最大的障碍--不过之后正式赛貌似就有英文了.(比赛貌似只有开俄文模式才看的到--) 时长1天,不随时间扣分.FallDream ...

  3. 【Git】CentOS7 通过源码安装Git

    yum源仓库里的Git版本更新不及时,最新版的Git是1.8.3,但是官方的最新版早已经更新到2.9.5.想要安装最新版本Git,只能下载源码进行安装 建议最好更新git为较新版本,便于使用 1.查看 ...

  4. 动态规划--Kin

    动态规划: 1.最大子序列和 2.LIS最长递增子序列 3.LCS最长公共子序列 4.矩阵连乘 5.数字金字塔 1.最大子序列和 #include<iostream> using name ...

  5. Spring中<context:annotation-config/>

    最近在研究Spring中<context:annotation-config/>配置的作用,现记录如下: <context:annotation-config/>的作用是向Sp ...

  6. Mybatis Generator 代码生成配置

    <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration ...

  7. MYSQL 表左连接 ON AND 和ON WHERE 的区别

    首先是针对左右连接,这里与inner join区分 在使用left join时,on and 和on where会有区别 1. on的条件是在连接生成临时表时使用的条件,以左表为基准 ,不管on中的条 ...

  8. SUSE11虚拟机安装与Oracle 11g安装

    SUSE11虚拟机安装与Oracle 11g安装 本文中所需所有参数均位于文末附录中 新建虚拟机,选择SUSE11 64位 启动虚拟机后,选择第二项安装 选择语言 跳过CD检查 选择全新安装 选择默认 ...

  9. Oracle中的列转行例子详解

    数据如下:name id张三 1,2,3 要求实现:name id张三 1张三 2张三 3 --创建临时表 create table tmp as(select '张三' name, '1,2,3' ...

  10. Java面试17|Java基础

    Linux上配置Java基础环境: https://www.cnblogs.com/kerrycode/archive/2015/08/27/4762921.html 1.final相关 (1)fin ...