Android Advanced NFC

译自:https://developer.android.google.cn/guide/topics/connectivity/nfc/advanced-nfc.html

This document describes advanced NFC topics, such as working with various tag technologies, writing to NFC tags, and foreground dispatching, which allows an application in the foreground to handle intents even when other applications filter for the same ones.

本文介绍了一个高级的NFC话题,比如在各种各样的标签技术下工作,写入NFC标签,前台调度,它允许即使其它的应用程序过滤了相同意图的情况下,在前台的应用程序也能处理该意图。

Working with Supported Tag Technologies

(译:在支持的标签技术下工作)

When working with NFC tags and Android-powered devices, the main format you use to read and write data on tags is NDEF. When a device scans a tag with NDEF data, Android provides support in parsing the message and delivering it in an NdefMessage when possible. There are cases, however, when you scan a tag that does not contain NDEF data or when the NDEF data could not be mapped to a MIME type or URI. In these cases, you need to open communication directly with the tag and read and write to it with your own protocol (in raw bytes). Android provides generic support for these use cases with the android.nfc.tech package, which is described in Table 1. You can use the getTechList() method to determine the technologies supported by the tag and create the corresponding TagTechnology object with one of classes provided by android.nfc.tech

当使用NFC标签和Android设备时,你读写标签上的数据的主要格式是NDEF。当设备扫描到一个带NDEF数据的标签时,Android会尽可能的为解析信息和分发NdefMessage提供支持。很多情况下,然而这并不包含当你扫描到一个并不包含NDEF数据的标签或者NDEF数据不能被映射到一个MIME类型或URI时。在这些情况下,你需要直接与标签通讯,并且使用你自己的协议(通过原始的字节)来读写它。Android通过android.nfc.tech包,为这些用例提供了通用的支持,它被描述在表1. 你可以使用getTechList() 方法来决定标签支持的技术和利用android.nfc.tech中的某个类来创建正确的标签技术对象。

Table 1. Supported tag technologies
(译:支持的标签技术)

ClassValueDescription
TagTechnologyThe interface that all tag technology classes must implement.

译:所有的标签技术类必须实现该接口。
NfcA1Provides access to NFC-A (ISO 14443-3A) properties and I/O operations.
NfcB2Provides access to NFC-B (ISO 14443-3B) properties and I/O operations.
NfcF4Provides access to NFC-F (JIS 6319-4) properties and I/O operations.
NfcV5Provides access to NFC-V (ISO 15693) properties and I/O operations.
IsoDep3Provides access to ISO-DEP (ISO 14443-4) properties and I/O operations.
Ndef6Provides access to NDEF data and operations on NFC tags that have been formatted as NDEF.

译:提供了访问NDEF数据和在已经被格式化为NDEF格式的NFC标签上操作的方法。
Ndef_Formatable7Provides a format operations for tags that may be NDEF formattable.

译:为可格式化为NDEF的标签提供了一种格式化的操作方法。


The following tag technlogies are not required to be supported by Android-powered devices.

下面的标签技术不是Android设备必须支持的。

Table 2. Optional supported tag technologies
(译:可选的支持的标签技术)

ClassValueDescription
Mifare_Classic8Provides access to MIFARE Classic properties and I/O operations, if this Android device supports MIFARE.

译:如果该Android设备支持MIFARE,提供访问MIFARE典型的属性和I/O操作
Mifare_Ultralight9Provides access to MIFARE Ultralight properties and I/O operations, if this Android device supports MIFARE.

译:如果该Android设备支持MIFARE,提供访问超轻量级MIFARE的属性和I/O操作

Working with tag technologies and the ACTION_TECH_DISCOVERED intent

(译:在各种标签技术和ACTION_TECH_DISCOVERED下工作)

When a device scans a tag that has NDEF data on it, but could not be mapped to a MIME or URI, the tag dispatch system tries to start an activity with the ACTION_TECH_DISCOVERED intent. The ACTION_TECH_DISCOVERED is also used when a tag with non-NDEF data is scanned. Having this fallback allows you to work with the data on the tag directly if the tag dispatch system could not parse it for you. The basic steps when working with tag technologies are as follows:

当设备扫描到一个具有NDEF数据的标签,但是它不能被映射到一个MIME或URI时,该标签调度系统就会尝试使用ACTION_TECH_DISCOVERED意图来启动activity。当没有NDEF数据的标签被扫描到时,也会使用ACTION_TECH_DISCOVERED。这种回退机制允许如果标签调度系统不能为你解析时,你也可以直接操作标签上的数据。使用标签技术的基本步骤如下:

  • Filter for an ACTION_TECH_DISCOVERED intent specifying the tag technologies that you want to handle. See Filtering for NFC intents for more information. In general, the tag dispatch system tries to start a ACTION_TECH_DISCOVERED intent when an NDEF message cannot be mapped to a MIME type or URI, or if the tag scanned did not contain NDEF data. For more information on how this is determined, see The Tag Dispatch System.

过滤一个你想要处理的指定标签技术的ACTION_TECH_DISCOVERED意图。参阅“Filtering for NFC intents”来获取更多信息。通常,在NDEF信息不能被映射到MIME类型或者URI或者如果扫描到的标签不包含NDEF数据时,标签调度系统会尝试启动一个ACTION_TECH_DISCOVERED意图。参阅“The Tag Dispatch System”来获取更多关于它是如何决定的信息。

  • When your application receives the intent, obtain the Tag object from the intent:

当你的应用程序接收到该意图时,可以从意图中获取标签对象。

Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
  • Obtain an instance of a TagTechnology, by calling one of the get factory methods of the classes in the android.nfc.tech package. You can enumerate the supported technologies of the tag by calling getTechList() before calling a get factory method. For example, to obtain an instance of MifareUltralight from a Tag, do the following:

通过调用android.nfc.tech包中的某个类的get的工厂方法,来获取一个标签技术的实例。在调用工厂方法之前,你可以通过调用getTechList()方法枚举标签支持的技术。例如,从标签中获取MifareUltralight实例的方法如下:

MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG));

Reading and writing to tags

(译:读写标签)

Reading and writing to an NFC tag involves obtaining the tag from the intent and opening communication with the tag. You must define your own protocol stack to read and write data to the tag. Keep in mind, however, that you can still read and write NDEF data when working directly with a tag. It is up to you how you want to structure things. The following example shows how to work with a MIFARE Ultralight tag.

向NFC标签中进行读写涉及到从intent中获取标签和与标签进行通信。你必须定义你自己的协议栈来向标签中读写数据。然而,记住直接操作标签时,你依然可以读写NDEF数据。由你来决定如何将事物进行结构化。下面的例子展示了如何操作一个MIFARE Ultralight 标签。

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;
    }
}

前面有说所有的标签技术都必须实现TagTechnology,相关的源码框架结构大体如下:

public final class MifareUltralight extends BasicTagTechnology { 
    ...
     /**
     * Read 4 pages (16 bytes).
     *
     * <p>The MIFARE Ultralight protocol always reads 4 pages at a time, to
     * reduce the number of commands required to read an entire tag.
     * <p>If a read spans past the last readable block, then the tag will
     * return pages that have been wrapped back to the first blocks. MIFARE
     * Ultralight tags have readable blocks 0x00 through 0x0F. So a read to
     * block offset 0x0E would return blocks 0x0E, 0x0F, 0x00, 0x01. MIFARE
     * Ultralight C tags have readable blocks 0x00 through 0x2B. So a read to
     * block 0x2A would return blocks 0x2A, 0x2B, 0x00, 0x01.
     *
     * <p>This is an I/O operation and will block until complete. It must
     * not be called from the main application thread. A blocked call will be canceled with
     * {@link IOException} if {@link #close} is called from another thread.
     *
     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
     *
     * @param pageOffset index of first page to read, starting from 0
     * @return 4 pages (16 bytes)
     * @throws TagLostException if the tag leaves the field
     * @throws IOException if there is an I/O failure, or the operation is canceled
     */
    public byte[] readPages(int pageOffset) throws IOException {
        validatePageIndex(pageOffset);
        checkConnected();

        byte[] cmd = { 0x30, (byte) pageOffset};
        return transceive(cmd, false);
    }

    /**
     * Write 1 page (4 bytes).
     *
     * <p>The MIFARE Ultralight protocol always writes 1 page at a time, to
     * minimize EEPROM write cycles.
     *
     * <p>This is an I/O operation and will block until complete. It must
     * not be called from the main application thread. A blocked call will be canceled with
     * {@link IOException} if {@link #close} is called from another thread.
     *
     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
     *
     * @param pageOffset index of page to write, starting from 0
     * @param data 4 bytes to write
     * @throws TagLostException if the tag leaves the field
     * @throws IOException if there is an I/O failure, or the operation is canceled
     */
    public void writePage(int pageOffset, byte[] data) throws IOException {
        validatePageIndex(pageOffset);
        checkConnected();

        byte[] cmd = new byte[data.length + 2];
        cmd[0] = (byte) 0xA2;
        cmd[1] = (byte) pageOffset;
        System.arraycopy(data, 0, cmd, 2, data.length);

        transceive(cmd, false);
    }
    ...
}
abstract class BasicTagTechnology implements TagTechnology { ... }
public interface TagTechnology extends Closeable {
    ...
    /**
     * Enable I/O operations to the tag from this {@link TagTechnology} object.
     * <p>May cause RF activity and may block. Must not be called
     * from the main application thread. A blocked call will be canceled with
     * {@link IOException} by calling {@link #close} from another thread.
     * <p>Only one {@link TagTechnology} object can be connected to a {@link Tag} at a time.
     * <p>Applications must call {@link #close} when I/O operations are complete.
     *
     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
     *
     * @see #close()
     * @throws TagLostException if the tag leaves the field
     * @throws IOException if there is an I/O failure, or connect is canceled
     */
    public void connect() throws IOException;

    /**
     * Disable I/O operations to the tag from this {@link TagTechnology} object, and release resources.
     * <p>Also causes all blocked I/O operations on other thread to be canceled and
     * return with {@link IOException}.
     *
     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
     *
     * @see #connect()
     */
    public void close() throws IOException;
    ...
}

Using the Foreground Dispatch System

(译:使用前台分发系统)

The foreground dispatch system allows an activity to intercept an intent and claim priority over other activities that handle the same intent. Using this system involves constructing a few data structures for the Android system to be able to send the appropriate intents to your application. To enable the foreground dispatch system:

前台分发系统允许activity拦截一个意图和声明一个高于其它能处理同样的意图的activities的优先级。使用该系统涉及到为Android系统构建少许的数据结构,使它能够为你的应用发送合适的意图。启用前台分发系统的方法如下:

  • Add the following code in the onCreate() method of your activity:

在你的activity的onCreate()方法中添加如下代码:

1、Create a PendingIntent object so the Android system can populate it with the details of the tag when it is scanned.

创建一个PendingIntent对象,以便当Android系统扫描到标签时,可以用标签的详细信息来填充PendingIntent对象。

PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);

2、Declare intent filters to handle the intents that you want to intercept. The foreground dispatch system checks the specified intent filters with the intent that is received when the device scans a tag. If it matches, then your application handles the intent. If it does not match, the foreground dispatch system falls back to the intent dispatch system. Specifying a null array of intent filters and technology filters, specifies that you want to filter for all tags that fallback to the TAG_DISCOVERED intent. The code snippet below handles all MIME types for NDEF_DISCOVERED. You should only handle the ones that you need.

声明意图过滤器来处理你想要拦截的意图。当扫描到一个标签的时候,前台调度系统会检查意图过滤器中是否有收到的指定的意图。如果符合,你的应用就会处理该意图。如果不符合,前台调度系统就会将其退送给意图调度系统。当回退到TAG_DISCOVERED 意图时,指定一个空数组的意图过滤器和技术过滤器,指明你想要过滤的所有标签。下面的代码处理了所有的NDEF_DISCOVERED的MIME类型。你可以只处理你所需要的那个。

IntentFilter ndef = new IntentFilter(NdcAdapter.ACTION_NEDF_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);
}
IntentFilter[] intentFiltersArray = new IntentFilter[] {ndef, };

3、Set up an array of tag technologies that your application wants to handle. Call the Object.class.getName() method to obtain the class of the technology that you want to support.

建立一个你的应用想要处理的标签技术数组。调用Object.class.getName() 来获取你想要支持的技术类。

String[][] techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
  • Override the following activity lifecycle callbacks and add logic to enable and disable the foreground dispatch when the activity loses (onPause()) and regains (onResume()) focus. enableForegroundDispatch() must be called from the main thread and only when the activity is in the foreground (calling in onResume() guarantees this). You also need to implement the onNewIntent callback to process the data from the scanned NFC tag.

覆盖下面的activity生命周期的回调方法,并且当activity失去焦点 (onPause()) 或重新获得焦点(onResume()) 时,从逻辑上启用和禁用前台调度器。enableForegroundDispatch()只能在主线程中并且activity在前台的时候被调用(可以在onResume() 中调用来保证它)。你也需要实现onNewIntent回调来处理扫描到的NFC标签中的数据。

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
}

See the ForegroundDispatch sample from API Demos for the complete sample

完整的例子,可以参阅API用例中的ForegroundDispatch

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值