高级 NFC

本文档介绍了高级的NFC主题,如各种标签技术,NFC标签写入和前台发布,它允许即使当其他应用程序过滤器相同的时候,应用程序在前台处理Intent。

Tag技术支持工作

当使NFC Tag和Android的供电设备生效,使用Tag来读取和写入数据的主要格式是NDEF,当设备扫描NDEF数据的Tag,Android提供支持解析的消息,并在可能的情况下将它传入一个NdefMessage,但是,在有些情况下,当你扫描不包含NDEF数据的Tag或当NDEF数据时无法映射到MIME类型或URI时。在那样的情况下,你需要直接与Tag建立沟通及用自己的协议(在原始字节)读写它,Android用android.nfc.tech包对那些情况提供了通用的支持,如表1中所描述的。您可以使用getTechList()方法来确定技术支持Tag,并且用由android.nfc.tech提供的类的其中一个创建相应TagTechnology对象.

表 1.支持的Tag技术


描述

TagTechnology

这个接口是下面所有tag technology类必须实现的。

NfcA

提供访问 NFC-A (ISO 14443-3A) 的属性和 I/O 操作

NfcB

提供访问 NFC-B (ISO 14443-3B) 的属性和 I/O 操作

NfcF

提供访问 NFC-F (JIS 6319-4) 的属性和 I/O 操作

NfcV

提供访问 NFC-V (ISO 15693) 的属性和 I/O 操作

IsoDep

提供访问 ISO-DEP (ISO 14443-4) 的属性和 I/O 操作

Ndef

提供对那些被格式化为NDEF的tag的数据的访问和其他操作

NdefFormatable

对那些可以被格式化成NDEF格式的tag提供一个格式化的操作

下面的Tag技术不要求被Android的供电设备支持。

表 2.可选的支持的Tag技术


描述

MifareClassic

如果android设备支持MIFARE,提供对MIFARE Classic目标的属性和I/O操作。

MifareUltralight

如果android设备支持MIFARE,提供对MIFAREUltralight目标的属性和I/O操作。

Tag技术工作和ACTION_TECH_DISCOVEREDIntent

当一个设备扫描一个有NDEF数据的Tag,但不能被映射到一个MIME或URI时,Tag发布系统试图启动一个Activity与ACTION_TECH_DISCOVEREDIntent。当非NDEF被扫描到时,ACTION_TECH_DISCOVERED的Tag也可以使用.如果Tag发布系统无法为你解析它,此回退让你直接进行扫描标签上的数据,基本步骤与标签技术如下:

过滤一个你想要处理的Tag技术的ACTION_TECH_DISCOVEREDintent. 获取更多信息请参阅过滤 NFC intents. 一般来说。 当一个NDEF消息不能被映射到MIME类型或者URI上时,Tag发布系统尝试启动一个ACTION_TECH_DISCOVEREDintent, 否则如果被扫描到的Tag不包含NDEF数据. 欲了解如何确定的更多信息,请参阅Tag发布系统.

当你的应用程序接收到该Intent, 从以图中获取Tag对象:

Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
  1. 通过调用在android.nfc.tech包的类中的一个getfactory方法,获取到TagTechnology的一个实例 . 你可以在调用一个getfactory方法以前,计算支持Tag技术通过调用getTechList(). 举例来说, 为了从一个Tag得到MifareUltralight的一个实例, 请执行下列操作:
MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG));

读取和写入Tags

读取和写入NFC tag涉及到从Intent中获取到Tag并创建与tag的连接. 你必须定义你自己的协议来读写数据到Tag. 然后,记住当直接使用Tag时你仍然能够读写NDEF数据. 你想要如何构建东西,它可以实现. 如下的例子演示如何使用一个MIFARE Ultralight tag.

package com.example.android.nfc;

import android.nfc.Tag;
import android.nfc.tech.MifareUltralight;
import android.util.Log;
import java.io.IOException;
import java.nio.charset.Charset;

public class MifareUltralightTagTester {

    private static final String TAG = MifareUltralightTagTester.class.getSimpleName();

    public void writeTag(Tag tag, String tagText) {
        MifareUltralight ultralight = MifareUltralight.get(tag);
        try {
            ultralight.connect();
            ultralight.writePage(4, "abcd".getBytes(Charset.forName("US-ASCII")));
            ultralight.writePage(5, "efgh".getBytes(Charset.forName("US-ASCII")));
            ultralight.writePage(6, "ijkl".getBytes(Charset.forName("US-ASCII")));
            ultralight.writePage(7, "mnop".getBytes(Charset.forName("US-ASCII")));
        } catch (IOException e) {
            Log.e(TAG, "IOException while closing MifareUltralight...", e);
        } finally {
            try {
                ultralight.close();
            } catch (IOException e) {
                Log.e(TAG, "IOException while closing MifareUltralight...", e);
            }
        }
    }

    public String readTag(Tag tag) {
        MifareUltralight mifare = MifareUltralight.get(tag);
        try {
            mifare.connect();
            byte[] payload = mifare.readPages(4);
            return new String(payload, Charset.forName("US-ASCII"));
        } catch (IOException e) {
            Log.e(TAG, "IOException while writing MifareUltralight
            message...", e);
        } finally {
            if (mifare != null) {
               try {
                   mifare.close();
               }
               catch (IOException e) {
                   Log.e(TAG, "Error closing tag...", e);
               }
            }
        }
        return null;
    }
}

使用前端发布系统


前端发布系统允许一个activity拦截一个 intent并且要求由于其他处理同样intent的activities. 使用这个系统涉及到为了Android系统能够发送合适的intents给你的应用程序而构建几个数据结构. 为了使前端发布系统有效:

  1. 在你的activity的

onCreate()

  1. 方法中添加如下代码:
  1. 创建一个

PendingIntent

  1. 对象, 以便系统可以在它被扫描到时,用tag的细节填充它
PendingIntent pendingIntent = PendingIntent.getActivity(
    this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
  1. 在Intent filters里声明你想要处理的Intent,一个tag被检测到时先检查前台发布系统,如果前台Activity符合Intent filter的要求,那么前台的Activity的将处理此Intent。如果不符合,前台发布系统将Intent转到Intent发布系统。如果指定了null的Intent filters,当任意tag被检测到时,你将收到TAG_DISCOVERED intent。下面的代码片断处理所有NDEF_DISCOVERED的MIME类型. 因此请注意你应该只处理你想要的Intent。
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
    try {
        ndef.addDataType("*/*");    /* Handles all MIME based dispatches.
                                       You should specify only the ones that you need. */
    }
    catch (MalformedMimeTypeException e) {
        throw new RuntimeException("fail", e);
    }
   intentFiltersArray = new IntentFilter[] {ndef, };
  1. 设置一个你程序要处理的Tag technologies的列表,调用Object.class.getName()方法来获得你想要支持处理的technology类。
techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
  1. 覆盖下面的方法来打开或关闭前台发布系统。比如onPause()和onResume()方法。必须在主线程里调用enableForegroundDispatch(Activity, PendingIntent, IntentFilter[], String[][])而且Activity在前台(可以在onResume()里调用来保证这点)。你也要覆盖onNewIntent回调来处理得到的NFC tag数据。
public void onPause() {
    super.onPause();
    mAdapter.disableForegroundDispatch(this);
}

public void onResume() {
    super.onResume();
    mAdapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray);
}

public void onNewIntent(Intent intent) {
    Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    //do something with tagFromIntent
}

从API Demos获取完整的示例演示ForegroundDispatch例子