使用案例研究进行 Android* 上的 NFC 应用开发





0/5 (0投票)
本文介绍了当前市场中基于 NFC 的技术和使用模式。然后,它还描述了如何在 Android 应用程序中使用 NFC。最后,它提出了两个案例研究,说明如何开发基于 NFC 的读写器应用程序。
引言
NFC(近场通信)是一种基于标准的短距离无线连接技术,可实现电子设备之间简单直观的双向交互。在两个 NFC 设备之间进行接触和通信非常方便。例如,借助智能手机集成的 NFC 技术,您可以轻松地触碰手机进行购物、分享名片、下载折扣券等等。未来将开发出许多基于 NFC 的新用途。
本文介绍了当前市场中基于 NFC 的技术和使用模式。然后,它还描述了如何在 Android 应用程序中使用 NFC。最后,它提出了两个案例研究,说明如何开发基于 NFC 的读写器应用程序。
NFC 技术架构
NFC 基于 13.56 MHz 的 RFID 技术,典型工作距离可达 10 厘米。数据交换速率高达 424 千比特/秒。与其他通信技术相比,NFC 最大的优势在于其使用快速简便。下图比较了 NFC 与其他通信技术。

NFC 技术有三种模式:NFC 卡模拟模式、点对点模式和读写器模式,如下图所示。

在卡模拟模式下,NFC 模拟带有安全模块的 RFID 集成电路 (IC) 卡,允许用户安全购物。在点对点模式下,您可以通过 NFC 连接在不同的 NFC 设备之间共享信息,例如名片。您还可以通过 NFC 连接快速设置 WiFi* 或蓝牙* 连接,并通过 WiFi 或蓝牙连接传输大文件。在读写器模式下,您可以使用支持 NFC 的设备读取 NFC 标签并启动智能任务。
每种模式将在下面更详细地讨论。
NFC 卡模拟模式
NFC 模块通常由两部分组成:NFC 控制器和安全元件 (SE)。NFC 控制器负责通信。SE 负责加密和解密敏感数据。

SE 通过 SWP(单线协议)或 DCLB(数字非接触式桥接)总线连接到 NFC 控制器。NFC 标准定义了主机和控制器之间的逻辑接口,允许它们通过射频场进行通信。内置应用程序或小型操作系统实现 SE,负责敏感数据的加密和解密。

实现 SE 的三种解决方案是
- 嵌入 SIM 卡
- 嵌入 SD 卡
- 嵌入 NFC 芯片

中国移动通信、沃达丰和 AT&T 等电信运营商通常更喜欢基于 SIM 卡的解决方案,他们鼓励用户免费将旧 SIM 卡更换为新的支持 NFC 的 SIM 卡。
NFC 点对点模式
两个支持 NFC 的设备可以轻松直接通信,共享名片等小文件。两个支持 NFC 的设备还可以相互共享配置的 .xml 文件,并建立蓝牙/WiFi 连接以共享大文件。在此模式下,不需要 SE 模块。
NFC 读写器模式
在此模式下,NFC 主机可以读/写 NFC 标签。一个很好的例子是阅读有用的
智能海报中的信息。用户可以访问链接查看广告和下载折扣券。


Android NFC 开发介绍
Android 通过两个包支持 NFC:android.nfc 和 android.nfc.tech。
android.nfc 包中的主要类是
NfcManager:Android 设备可用于管理所有指示的 NFC 适配器,但由于大多数 Android 设备只支持一个 NFC 适配器,因此 NfcManager 通常直接通过 getDefaultAdapter 调用以获取特定的手机适配器。
NfcAdapter:它作为 NFC 代理,类似于计算机中安装的网络适配器,手机通过它访问 NFC 硬件以启动 NFC 通信。
NDEF:NFC 标准定义了一种通用数据格式,称为 NFC 数据交换格式 (NDEF),它可以存储和传输各种项目,从任何 MIME 类型对象到超短 RTD 文档,例如 URL。NdefMessage 和 NdefRecord 是 NFC 论坛定义的两种 NDEF 数据格式,将在示例代码中使用。
Tag:Android 定义它表示标签、卡片等被动对象。当设备检测到标签时,Android 将创建一个标签对象,然后将其放入 Intent 对象中,最后将其发送到适当的 Activity。
android.nfc.tech 包还包含许多重要的子类。这些子类提供对标签技术功能(包括读写操作)的访问。根据所使用的技术类型,这些类分为不同的类别,例如:NfcA、NfcB、NfcF、MifareClassic 等。
当手机开启 NFC 并检测到 TAG 后,TAG 分发系统将自动创建一个包含 NFC TAG 信息的 intent 包。如果手机有多个应用程序可以处理此 intent,将弹出一个对话框询问用户选择哪个 TAG activity。TAG 分发系统定义了三种类型的 intent。它们按优先级降序排列
NDEF_DISCOVERED, TECH_DISCOVERED, TAG_DISCOVERED
这里我们使用 action intent-filter 类型来处理从 TECH_DISCOVERED 到 ACTION_TECH_DISCOVERED 的所有类型。文件 nfc_tech_filter.xml 用于文件中 TAG 定义的类型。有关详细信息,请参阅Android 文档。下图显示了手机检测到 TAG 时匹配过程的 Activity。

案例研究:开发基于 NFC 的读写器应用程序
以下演示展示了 NFC 标签的读写器功能。在访问设备的 NFC 硬件并正确处理 NFC intent 之前,请在您的 AndroidManifest.xml 文件中声明这些项目
<uses-permission android:name="android.permission.NFC" />
您的应用程序必须支持的最低 SDK 版本是级别 10,因此请在您的 AndroidManifest.xml 文件中声明这些项目
<uses-sdk android:minSdkVersion="10"/>
In the onCreate function,you can apply the NfcAdapter:
public void onCreate(Bundle savedInstanceState) {
……
adapter = NfcAdapter.getDefaultAdapter(this);
……
}
以下 Intent 回调显示了读取器功能。如果系统广播 Intent 等于 NfcAdapter.ACTION_TAG_DISCOVERED,那么您可以读取标签中的信息并显示它。
@Override
	protected void onNewIntent(Intent intent){
		if(NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())){
		mytag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);  // get the detected tag
		Parcelable[] msgs =
intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
			NdefRecord firstRecord = ((NdefMessage)msgs[0]).getRecords()[0];
			byte[] payload = firstRecord.getPayload();
			int payloadLength = payload.length;
			int langLength = payload[0];
			int textLength = payloadLength - langLength - 1;
			byte[] text = new byte[textLength];
			System.arraycopy(payload, 1+langLength, text, 0, textLength);
			Toast.makeText(this, this.getString(R.string.ok_detection)+new String(text), Toast.LENGTH_LONG).show();
					}
	}
以下代码显示了写入器功能。在确定 mytag 的值之前,您必须知道是否检测到标签,然后将信息写入 mytag。
If (mytag==Null){
	……
}
else{
……
write(message.getText().toString(),mytag);
……
}
	private void write(String text, Tag tag) throws IOException, FormatException {
		NdefRecord[] records = { createRecord(text) };
		NdefMessage  message = new NdefMessage(records);
// Get an instance of Ndef for the tag.
		Ndef ndef = Ndef.get(tag); // Enable I/O
		ndef.connect(); // Write the message
		ndef.writeNdefMessage(message); // Close the connection
		ndef.close();
	}
根据从标签读取的信息,您可以执行更多操作,例如启动智能任务、访问网站等。
案例研究:开发使用 MifareClassic 卡的基于 NFC 的应用程序
在此演示中,我们使用 Mifare 卡进行数据读取测试,并使用卡的 TAG 类型 MifareClassic。MifareClassic 卡常用于许多场景,例如身份证、公交卡等。传统的 MifareClassic 卡的存储空间分为 16 个区域(扇区),每个区域有四个块(块),每个块可以存储 16 字节的数据。
每个区域的最后一个块称为 Trailer,主要用于存储用于读写数据的本地块密钥。它有两个密钥:A 和 B,每个密钥长度为 6 字节,默认值通常由 MifareClassic.KEY_DEFAULT 定义为全键 FF 或 0。
因此,在写入 Mifare 卡时,首先需要具有正确的密钥值(起到保护作用),并且在用户读写该区域的数据之前必须进行成功的身份验证。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"   
    package="org.reno"   
    android:versionCode="1"   
    android:versionName="1.0" >   
    <uses-permission android:name="android.permission.NFC" />   
    <uses-sdk android:minSdkVersion="14" />   
    <uses-feature android:name="android.hardware.nfc" android:required="true" />   
    <application   
        android:icon="@drawable/ic_launcher"   
        android:label="@string/app_name" >   
        <activity   
            android:name="org.reno.Beam"   
            android:label="@string/app_name"   
            android:launchMode="singleTop" >   
            <intent-filter>   
                <action android:name="android.intent.action.MAIN" />   
   
                <category android:name="android.intent.category.LAUNCHER" />   
            </intent-filter>   
            <intent-filter>   
                <action android:name="android.nfc.action.TECH_DISCOVERED" />   
            </intent-filter>   
            <meta-data   
                android:name="android.nfc.action.TECH_DISCOVERED"   
                android:resource="@xml/nfc_tech_filter" />   
        </activity>  
    </application>   
</manifest>
res/xml/nfc_tech_filter.xml:
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> 
    <tech-list> 
       <tech>android.nfc.tech.MifareClassic</tech> 
    </tech-list> 
</resources>
以下是读取 MifareClassic 卡的示例
     private void processIntent(Intent intent) {    
        Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);   
        for (String tech : tagFromIntent.getTechList()) {   
            System.out.println(tech);   
        }   
        boolean auth = false;   
        MifareClassic mfc = MifareClassic.get(tagFromIntent);   
        try {   
            String metaInfo = "";   
            //Enable I/O operations to the tag from this TagTechnology object.   
            mfc.connect();   
            int type = mfc.getType(); 
            int sectorCount = mfc.getSectorCount();   
            String typeS = "";   
            switch (type) {   
            case MifareClassic.TYPE_CLASSIC:   
                typeS = "TYPE_CLASSIC";   
                break;   
            case MifareClassic.TYPE_PLUS:   
                typeS = "TYPE_PLUS";   
                break;   
            case MifareClassic.TYPE_PRO:   
                typeS = "TYPE_PRO";   
                break;   
            case MifareClassic.TYPE_UNKNOWN:   
                typeS = "TYPE_UNKNOWN";   
                break;   
            }   
            metaInfo += "Card type:" + typeS + "n with" + sectorCount + " Sectorsn, "   
                    + mfc.getBlockCount() + " BlocksnStorage Space: " + mfc.getSize() + "Bn";   
            for (int j = 0; j < sectorCount; j++) {   
                //Authenticate a sector with key A.   
                auth = mfc.authenticateSectorWithKeyA(j,   
                        MifareClassic.KEY_DEFAULT);   
                int bCount;   
                int bIndex;   
                if (auth) {   
                    metaInfo += "Sector " + j + ": Verified successfullyn";   
                    bCount = mfc.getBlockCountInSector(j);   
                    bIndex = mfc.sectorToBlock(j);   
                    for (int i = 0; i < bCount; i++) {   
                        byte[] data = mfc.readBlock(bIndex);   
                        metaInfo += "Block " + bIndex + " : "   
                                + bytesToHexString(data) + "n";   
                        bIndex++;   
                    }   
                } else {   
                    metaInfo += "Sector " + j + ": Verified failuren";   
                }   
            }   
            promt.setText(metaInfo);   
        } catch (Exception e) {   
            e.printStackTrace();   
        }   
    }
摘要
配备 NFC 的智能手机让个人无需随身携带票根或停车证,即可将所需的一切掌握在指尖。该技术甚至可以与朋友连接,共享信息、玩游戏和传输数据。NFC 致力于兼顾工作和娱乐,这是其取得成功并在未来便利我们生活的关键因素。
关于作者
王松月和张亮是英特尔软件与服务集团的应用工程师,专注于移动应用赋能,包括 Android 应用开发和 x86 设备优化,以及 Web HTML5 应用开发。
其他相关文章和资源
- Gayathri Murali 的《Intel 平台上的 Android》 - Bay Area Android Fest
- Intel® Atom™ x86 镜像 for Android* 4.4 KitKat 安装说明 - 手动
- 编码 Android* 游戏应用以支持 Intel x86?这里有一些示例!
- Intel for Android* 开发者学习系列 #7:为 Intel® 架构创建和移植基于 NDK 的 Android* 应用程序
- 使用案例研究进行 Android* 上的 NFC 应用开发
- 在 Android* 商业应用程序中实现地图和地理围栏功能
- 自动 Android* 应用程序测试
- 如何在 Intel 架构上优化您的 Android* 应用 (NDK) - 两分钟搞定
- 如何设置 NDK 项目以编译适用于多个目标平台的应用

