Android 近场通信(NFC)

Near Field Communication (NFC) is a set of short-range wireless technologies, typically requiring a distance of 4cm or less. NFC operates at 13.56mhz, and at rates ranging from 106 kbit/s to 848 kbit/s. NFC communication always involves an initiator and a target. The initiator actively generates an RF field that can power a passive target. This enables NFC targets to take very simple form factors such as tags, stickers or cards that do not require power. NFC peer-to-peer communication is also possible, where both devices are powered.

Compared to other wireless technologies such as Bluetooth or WiFi, NFC provides much lower bandwidth and range, but enables low-cost, un-powered targets and does not require discovery or pairing. Interactions can be initiated with just a tap.

An Android device with NFC hardware will typically act as an initiator when the screen is on. This mode is also known as NFC reader/writer. It will actively look for NFC tags and start activities to handle them. Android 2.3.3 also has some limited P2P support.

Tags can range in complexity, simple tags just offer read/write semantics, sometimes with one-time-programmable areas to make the card read-only. More complex tags offer math operations, and have cryptographic hardware to authenticate access to a sector. The most sophisticated tags contain operating environments, allowing complex interactions with code executing on the tag.

API Overview

The android.nfc package contains the high-level classes to interact with the local device's NFC adapter, to represent discovered tags, and to use the NDEF data format.

ClassDescription
NfcManagerA high level manager class that enumerates the NFC adapters on this Android device. Since most Android devices only have one NFC adapter, you can just use the static helper getDefaultAdapter(Context) for most situations.
NfcAdapterRepresents the local NFC adapter. Defines the intent's used to request tag dispatch to your activity, and provides methods to register for foreground tag dispatch and foreground NDEF push. Foreground NDEF push is the only peer-to-peer support that is currently provided in Android.
NdefMessage and NdefRecordNDEF is an NFC Forum defined data structure, designed to efficiently store data on NFC tags, such as text, URL's, and other MIME types. A NdefMessage acts as a container for the data that you want to transmit or read. One NdefMessage object contains zero or more NdefRecords. Each NDEF record has a type such as text, URL, smart poster, or any MIME data. The type of the first NDEF record in the NDEF message is used to dispatch a tag to an activity on Android.
TagRepresents a passive NFC target. These can come in many form factors such as a tag, card, key fob, or even a phone doing card emulation. When a tag is discovered, a Tag object is created and wrapped inside an Intent. The NFC dispatch system sends the intent to a compatible activity using startActivity(). You can use the getTechList() method to determine the technologies supported by this tag and create the corresponding TagTechnology object with one of classes provided by android.nfc.tech.

The android.nfc.tech package contains classes to query properties and perform I/O operations on a tag. The classes are divided to represent different NFC technologies that can be available on a Tag:

ClassDescription
TagTechnologyThe interface that all tag technology classes must implement.
NfcAProvides access to NFC-A (ISO 14443-3A) properties and I/O operations.
NfcBProvides access to NFC-B (ISO 14443-3B) properties and I/O operations.
NfcFProvides access to NFC-F (JIS 6319-4) properties and I/O operations.
NfcVProvides access to NFC-V (ISO 15693) properties and I/O operations.
IsoDepProvides access to ISO-DEP (ISO 14443-4) properties and I/O operations.
NdefProvides access to NDEF data and operations on NFC tags that have been formatted as NDEF.
NdefFormatableProvides a format operations for tags that may be NDEF formattable.
MifareClassicProvides access to MIFARE Classic properties and I/O operations, if this Android device supports MIFARE.
MifareUltralightProvides access to MIFARE Ultralight properties and I/O operations, if this Android device supports MIFARE.

Declaring Android Manifest elements

Before you can access a device's NFC hardware and properly handle NFC intents, declare these items in your AndroidManifest.xml file:

  1. The NFC <uses-permission> element to access the NFC hardware:
    <uses-permission android:name="android.permission.NFC" />
  2. The minimum SDK version that your application can support. API level 9 only supports limited tag dispatch via ACTION_TAG_DISCOVERED, and only gives access to NDEF messages via the EXTRA_NDEF_MESSAGES extra. No other tag properties or I/O operations are accessible. You probably want to use API level 10 which includes comprehensive reader/writer support.
    <uses-sdk android:minSdkVersion="10"/>
  3. The uses-feature element so that your application can show up in the Android Market for devices that have NFC hardware:
    <uses-feature android:name="android.hardware.nfc" android:required="true" />
  4. The NFC intent filter to tell the Android system your Activity can handle NFC data. Specify one or more of these three intent filters:
    <intent-filter>
      <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
      <data android:mimeType="mime/type" />
    </intent-filter>
    
    <intent-filter>
      <action android:name="android.nfc.action.TECH_DISCOVERED"/>
      <meta-data android:name="android.nfc.action.TECH_DISCOVERED"
                    android:resource="@xml/nfc_tech_filter.xml" />
    </intent-filter>
    
    <intent-filter>
      <action android:name="android.nfc.action.TAG_DISCOVERED"/>
    </intent-filter>

    The three intent filters are prioritized and behave in specific ways. Declare only the ones that your Activity needs to handle. For more information on how to handle these filters, see the section about The Tag Dispatch System.

View the AndroidManifest.xml from the NFCDemo sample to see a complete example.

The Tag Dispatch System

When an Android device scans an NFC tag, the desired behavior is to have the most appropriate Activity handle the intent without asking the user what application to use. Because devices scan NFC tags at a very short range, it is likely that making users manually select an Activity forces them to move the device away from the tag and break the connection. You should develop your Activity to only handle the NFC tags that your Activity cares about to prevent the Activity Chooser from appearing. Android provides two systems to help you correctly identify an NFC tag that your Activity should handle: the Intent dispatch system and the foreground Activity dispatch system.

The intent dispatch system checks the intent filters of all the Activities along with the types of data that the Activities support to find the best Activity that can handle the NFC tag. If multiple Activities specify the same intent filter and data to handle, then the Activity Chooser is presented to the user as a last resort.

The foreground dispatch system allows an Activity application to override the intent dispatch system and have priority when an NFC tag is scanned. The Activity handling the request must be running in the foreground of the device. When an NFC tag is scanned and matches the intent and data type that the foreground dispatch Activity defines, the intent is immediately sent to the Activity even if another Activity can handle the intent. If the Activity cannot handle the intent, the foreground dispatch system falls back to the intent dispatch system.

Using the intent dispatch system

The intent dispatch system specifies three intents that each have a priority. The intents that start when a device scans a tag depend on the type of tag scanned. In general, the intents are started in the following manner:

  • android.nfc.action.NDEF_DISCOVERED: This intent starts when a tag that contains an NDEF payload is scanned. This is the highest priority intent. The Android system does not let you specify this intent generically to handle all data types. You must specify <data> elements in the AndroidManifest.xml along with this intent to correctly handle NFC tags that start this intent. For example, to handle a NDEF_DISCOVERED intent that contains plain text, specify the following filter in your AndroidManifest.xml file:
    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
        <data android:mimeType="text/plain" />
    </intent-filter>

    If the NDEF_DISCOVERED intent is started, the TECH_DISCOVERED and TAG_DISCOVERED intents are not started. This intent does not start if an unknown tag is scanned or if the tag does not contain an NDEF payload.

  • android.nfc.action.TECH_DISCOVERED: If the NDEF_DISCOVERED intent does not start or is not filtered by any Activity on the device, this intent starts if the tag is known. The TECH_DISCOVERED intent requires that you specify the technologies that you want to support in an XML resource file. For more information, see the section about Specifying tag technologies to handle.
  • android.nfc.action.TAG_DISCOVERED: This intent starts if no Activities handle the NDEF_DISCOVERED and TECH_DISCOVERED intents or if the tag that is scanned is unknown.
Specifying tag technologies to handle

If your Activity declares the android.nfc.action.TECH_DISCOVERED intent in your AndroidManifest.xml file, you must create an XML resource file that specifies the technologies that your Activity supports within a tech-list set. Your Activity is considered a match if a tech-list set is a subset of the technologies that are supported by the tag, which you can obtain by calling getTechList().

For example, if the tag that is scanned supports MifareClassic, NdefFormatable, and NfcA, your tech-list set must specify all three, two, or one of the technologies (and nothing else) in order for your Activity to be matched.

The following sample defines all of the technologies. You can remove the ones that you do not need. Save this file (you can name it anything you wish) in the <project-root>/res/xml folder.

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.IsoDep</tech>
        <tech>android.nfc.tech.NfcA</tech>        
        <tech>android.nfc.tech.NfcB</tech>
        <tech>android.nfc.tech.NfcF</tech>
        <tech>android.nfc.tech.NfcV</tech>
        <tech>android.nfc.tech.Ndef</tech>
        <tech>android.nfc.tech.NdefFormatable</tech>
        <tech>android.nfc.tech.MifareClassic</tech>
        <tech>android.nfc.tech.MifareUltralight</tech>
    </tech-list>
</resources>

You can also specify multiple tech-list sets. Each of the tech-list sets is considered independently, and your Activity is considered a match if any single tech-list set is a subset of the technologies that are returned by getTechList(). This provides AND and OR semantics for matching technologies. The following example matches tags that can support the NfcA and Ndef technologies or can support the NfcB and Ndef technologies:

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.NfcA</tech>        
        <tech>android.nfc.tech.Ndef</tech>
    </tech-list>
</resources>

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.NfcB</tech>        
        <tech>android.nfc.tech.Ndef</tech>
    </tech-list>
</resources>

In your AndroidManifest.xml file, specify the resource file that you just created in the <meta-data> element inside the <activity> element like in the following example:

<activity>
...
<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>

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. The system is easy to use and 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:

  1. Add the following code in the onCreate() method of your Activity:
    1. Create a PendingIntent object so the Android system can populate it with the details of the tag when it is scanned
      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 they match, 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 for the technology filters, you receive a TAG_DISCOVERED intent for all tags discovered. Note that the snippet below handles all MIME types. You should only handle the ones that you need.
          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,
              };
    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.
      
        techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
        
  2. 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(Activity, PendingIntent, IntentFilter[], String[][]) 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.
    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.

Working with Data on NFC Tags

Data on NFC tags are encoded in raw bytes, so you must convert the bytes to something human readable if you are presenting the data to the user. When writing to NFC tags, you must write them in bytes as well. Android provides APIs to help write messages that conform to the NDEF standard, which was developed by the NFC Forum to standardized data on tags. Using this standard ensures that your data will be supported by all Android NFC devices if you are writing to tags. However, many tag technologies use their own standard for storing data and are supported by Android as well, but you have to implement your own protocol stack to read and write to these tags. You can find a full list of the supported technologies in android.nfc.tech and an overview of the technologies in the TagTechnology interface. This section is a brief overview of how to work with NDEF messages in the context of the Android system. It is not meant to be a complete discussion of the NDEF specification, but highlights the main things that you need to be aware of when working with NDEF messages in Android.

To facilitate working with NDEF messages, Android provides the NdefRecord and NdefMessage to encapsulate the raw bytes that represent NDEF messages. An NdefMessage is the container for zero or more NdefRecords. Each NdefRecord has its own unique type name format, record type, and ID to distinguish them from other records within the same NdefMessage. You can store different types of records of varying length in a single NdefMessage. The size constraint of the NFC tag determines how big your NdefMessage can be.

Tags that support the Ndef and NdefFormatable technologies return and accept NdefMessage objects as parameters for read and write operations. You need to create your own logic to read and write bytes for other tag technologies in android.nfc.tech.

You can download technical specifications for different types of NDEF message standards, such as plain text and Smart Posters, at the NFC Forum website. The NFCDemo sample application also declares sample plain text and SmartPoster NDEF messages.

Reading an NFC Tag

When a device comes in proximity to an NFC tag, the appropriate intent is started on the device, notifying interested applications that a NFC tag was scanned. By previously declaring the appropriate intent filter in your AndroidManifest.xml file or using foreground dispatching, your application can request to handle the intent.

The following method (slightly modified from the NFCDemo sample application), handles the TAG_DISCOVERED intent and iterates through an array obtained from the intent that contains the NDEF payload:

NdefMessage[] getNdefMessages(Intent intent) {
    // Parse the intent
    NdefMessage[] msgs = null;
    String action = intent.getAction();
    if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) {
        Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
        if (rawMsgs != null) {
            msgs = new NdefMessage[rawMsgs.length];
            for (int i = 0; i < rawMsgs.length; i++) {
                msgs[i] = (NdefMessage) rawMsgs[i];
            }
        }
        else {
        // Unknown tag type
            byte[] empty = new byte[] {};
            NdefRecord record = new NdefRecord(NdefRecord.TNF_UNKNOWN, empty, empty, empty);
            NdefMessage msg = new NdefMessage(new NdefRecord[] {record});
            msgs = new NdefMessage[] {msg};
        }
    }        
    else {
        Log.e(TAG, "Unknown intent " + intent);
        finish();
    }
    return msgs;
}

Keep in mind that the data that the device reads is in bytes, so you must implement your own logic if you need to present the data in a readable format to the user. The classes in com.example.android.nfc.record of the NFCDemo sample show you how to parse some common types of NDEF messages such as plain text or a SmartPoster.

Writing to an NFC Tag

Writing to an NFC tag involves constructing your NDEF message in bytes and using the appropriate tag technology for the tag that you are writing to. The following code sample shows you how to write a simple text message to a NdefFormatable tag:

NdefFormatable tag = NdefFormatable.get(t);
Locale locale = Locale.US;
final byte[] langBytes = locale.getLanguage().getBytes(Charsets.US_ASCII);
String text = "Tag, you're it!";
final byte[] textBytes = text.getBytes(Charsets.UTF_8);
final int utfBit = 0;
final char status = (char) (utfBit + langBytes.length);
final byte[] data = Bytes.concat(new byte[] {(byte) status}, langBytes, textBytes);
NdefRecord record = NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data);
try {
    NdefRecord[] records = {text};
    NdefMessage message = new NdefMessage(records);
    tag.connect();
    tag.format(message);
}
catch (Exception e){
    //do error handling
}

Peer-to-Peer Data Exchange

Support for simple peer-to-peer data exchange is supported by the foreground push feature, which is enabled with the enableForegroundNdefPush(Activity, NdefMessage) method. To use this feature:

  • The Activity that is pushing the data must be in the foreground
  • You must encapsulate the data that you are sending in an NdefMessage object
  • The NFC device that is receiving the pushed data (the scanned device) must support the com.android.npp NDEF push protocol, which is optional for Android devices.

If your Activity enables the foreground push feature and is in the foreground, the standard intent dispatch system is disabled. However, if your Activity also enables foreground dispatching, then it can still scan tags that match the intent filters set in the foreground dispatching.

To enable foreground dispatching:

  1. Create an NdefMessage that contains the NdefRecords that you want to push onto the other device.
  2. Implement the onResume() and onPause() callbacks in your Activity to appropriately handle the foreground pushing lifecycle. You must call enableForegroundNdefPush(Activity, NdefMessage) from the main thread and only when the activity is in the foreground (calling in onResume() guarantees this).
    public void onResume() {
        super.onResume();
        if (mAdapter != null)
            mAdapter.enableForegroundNdefPush(this, myNdefMessage);
    }
    public void onPause() {
        super.onPause();
        if (mAdapter != null)
            mAdapter.disableForegroundNdefPush(this);
    }
When the Activity is in the foreground, you can now tap the device to another device and push the data to it. See the ForegroundNdefPush sample in API Demos for a simple example of peer-to-peer data exchange
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值