Android开发者文章:近场通信---高级NFC编程

本文译自:http://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc.html

本文介绍一些高级的NFC专题,如多样的NFC标签技术、编写NFC标签、以及前台调度,前台调度允许在前台的应用程序优先调度Intent事件,即使还有其他的过滤同样的Intent事件的应用程序存在。

Android所支持的NFC标签技术

在使用NFC标签和Android设备来进行工作的时候,使用的读写NFC标签上数据的主要格式是NDEF。当设备扫描到带有NDEF的数据时,Android会提供对消息解析的支持,并在可能的时候,会以NdefMessage对象的形式来发送它。但是,有些情况下,设备扫描到的NFC标签没有包含NDEF数据,或者该NDEF数据没有被映射到MIME类型或URI。在这些情况下,你需要打开跟NFC标签的通信,并用自己的协议(原始的字节形式)来读写它。Android用android.nfc.tech包提供了对这些情况的一般性支持,这个包在下表1中介绍。你能够使用getTechList()方法来判断NFC标签所支持的的技术,并且用android.nfc.tech提供的一个类来创建对应的TagTechnology对象。

表1.NFC标签所支持的技术

介绍

TagTechnology

所有的NFC标签技术类必须实现的接口。

NfcA

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

NfcB

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

NfcF

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

NfcV

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

IsoDep

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

Ndef

提供对NDEF格式的NFC标签上的NDEF数据和操作的访问。

NdefFormatable

提供了对可以被NDEF格式化的NFC标签的格式化操作。

表2.可选的NFC标签所支持的技术

介绍

MifareClassic

如果Android设备支持MIFARE,那么它提供了对经典的MIFARE类型标签属性和I/O操作的访问。

MifareUltralight

如果Android设备支持MIFARE,那么它提供了对超薄的MIFARE类型标签属性和I/O操作的访问。

NFC标签和ACTION_TECH_DISCOVERED类型的Intent协同工作

当设备扫描到带有NDEF数据的NFC标签,但却不能映射到MIME或URI时,NFC标签调度系统就尝试使用ACTION_TECH_DISCOVERED类型的Intent来启动一个Activity。在被扫描到的NFC标签上没有NDEF数据时,也会使用ACTION_TECH_DISCOVERED类型的Intent。有了这种回退机制,如果调度系统不能够帮你解析数据,那么你就可以直接使用NFC标签上数据来工作。基本步骤如下:

1. 给你希望处理的NFC标签指定ACTION_TECH_DISCOVERED类型的Intent过滤器。更多信息请看“NFC的Intent过滤”。通常,在NDEF消息不能被映射到MIME类型或URI时,或者被扫描到的NFC标签不包含NDEF数据时,NFC标签调度系统会尝试启动一个ACTION_TECH_DISCOVERED类型的Intent。更多信息,请看“NFC标签调度系统”。

2. 应用程序接收到Intent对象时,从该Intent对象中获取Tag对象:

Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);

3. 通过调用android.nfc.tech包中对应类的一个get工厂方法,来获取一个TagTechnology对象实例。在调用get工厂方法之前,通过调用getTechList()方法来枚举NFC标签所支持的技术。例如,用下列方法从Tag对象中获取MifareUltralight对象实例:

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

读写NFC标签 读写NFC标签,要涉及到从Intent对象中获取标签,并要打开与标签的通信。要读写NFC标签数据,你必须要定义自己的协议栈。但是,要记住在直接使用NFC标签工作时,你依然能够读写NDEF数据。这是你想要如何构建的事情。下例演示了如何使用MIFARE超薄标签来工作:

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;  
publicclassMifareUltralightTagTester{  
    privatestaticfinalString TAG =MifareUltralightTagTester.class.getSimpleName();  
    publicvoid 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);  
            }  
        }  
    }  
    publicString readTag(Tag tag){  
        MifareUltralight mifare =MifareUltralight.get(tag);  
        try{  
            mifare.connect();  
            byte\[\] payload = mifare.readPages(4);  
            returnnewString(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);  
               }  
            }  
        }  
        returnnull;  
    }  
}

使用前台调度系统

前台调度系统允许一个Activity拦截Intent对象,并且声明该Activity的优先级要比其他的处理相同Intent对象的Activity高。使用这个系统涉及到为Android系统构建一些数据结构,以便能够把相应的Intent对象发送给你的应用程序,以下是启用前台调度系统的步骤:

1.  在你的Activity的onCreate()方法中添加下列代码:

     A.  创建一个PendingIntent对象,以便Android系统能够在扫描到NFC标签时,用它来封装NFC标签的详细信息。

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

     B.  声明你想要截获处理的Intent对象的Intent过滤器。前台调度系统会在设备扫描到NFC标签时,用声明的Intent过滤器来检查接收到的Intent对象。如果匹配就会让你的应用程序来处理这个Intent对象,如果不匹配,前台调度系统会回退到Intent调度系统。如果Intent过滤器和技术过滤器的数组指定了null,那么就说明你要过滤所有的退回到TAG_DISCOVERED类型的Intent对象的标签。以下代码会用于处理所有的NDEF_DISCOVERED的MIME类型。只有在需要的时候才做这种处理:

IntentFilter ndef =newIntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);  
   try{  
       ndef.addDataType("*/*");    /* Handles all MIME based dispatches.  
                                      You should specify only the ones that you need. */  
   }  
   catch(MalformedMimeTypeException e){  
       thrownewRuntimeException("fail", e);  
   }  
  intentFiltersArray =newIntentFilter\[\]{ndef,};

     C.  建立一个应用程序希望处理的NFC标签技术的数组。调用Object.class.getName()方法来获取你想要支持的技术的类:

techListsArray = new String[][] { new String[] { NfcF.class.getName() } };

2.  重写下列Activity生命周期的回调方法,并且添加逻辑在Activity挂起(onPause())和获得焦点(onResume())时,来启用和禁用前台调度。enableForegroundDispatch()方法必须在主线程中被调用,并且只有在该Activity在前台的时候(要保证在onResume()方法中调用这个方法)。你还需要实现onNewIntent回调方法来处理扫描到的NFC标签的数据:

publicvoid onPause(){  
    super.onPause();  
    mAdapter.disableForegroundDispatch(this);  
}  
publicvoid onResume(){  
    super.onResume();  
    mAdapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray);  
}  
publicvoid onNewIntent(Intent intent){  
    Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);  
    //do something with tagFromIntent  
}

完整的示例请看API Demo中的ForegroundDispatch