nfc入门

来源:互联网 发布:paperpass查重软件 编辑:程序博客网 时间:2024/06/06 00:30

基础

        参考

        利用nfc读取公交卡

        nfc:近距离无线通信(Near Field Communication),距离一般要小于4cm。android设备(需要支持nfc技术)和nfc设备(文档中称为tag)进行少量的数据交换。

权限及feature配置

        需要nfc权限,如下:

<uses-permission android:name="android.permission.NFC"/>
也可以配置uses-feature。但,如果Nfc并不是app的主要功能,不建议在清单文件中配置。
<uses-feature android:name="android.hardware.nfc" android:required="true" />
        如果没有配置<uses-feature>可能导致不支持nfc功能的android设备安装上了该应用。可以使用如下代码进行检测判断
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);        if (adapter == null)            Toast.makeText(MainActivity.this, "nfc不支持", Toast.LENGTH_SHORT).show();        if (adapter != null && !adapter.isEnabled()) {            Toast.makeText(MainActivity.this, "nfc未打开", Toast.LENGTH_SHORT).show();        }

action的配置

        当android设备扫描到有nfc设备时(未锁屏状态下,android设备会不停地扫描nfc设备,除非用户在手机设置中关闭了nfc功能),系统会发送一个intent。因此,为了获取到该intent, 需要在相应的activity配置中加上<intent-filter>。该intent一共有以下三个action:

<action android:name="android.nfc.action.NDEF_DISCOVERED"/><action android:name="android.nfc.action.TECH_DISCOVERED" /><action android:name="android.nfc.action.TAG_DISCOVERED" />

三个action的匹配顺序如下:


NDEF_DISCOVERED

        当nfc设备(tag)中存储的数据为ndef格式并且系统可以识别数据的类型(MIME Type或者 URI)时,intent的action为该值。如:

匹配纯文本

<intent-filter>    <action android:name="android.nfc.action.NDEF_DISCOVERED"/>    <category android:name="android.intent.category.DEFAULT"/>    <data android:mimeType="text/plain" /><!--MIME Type--></intent-filter>
匹配链接http://developer.android.com/index.html:
<intent-filter>    <action android:name="android.nfc.action.NDEF_DISCOVERED"/>    <category android:name="android.intent.category.DEFAULT"/>   <data android:scheme="http"              android:host="developer.android.com"              android:pathPrefix="/index.html" /><!--uri--></intent-filter>
        在上面配置intent-filter时,在<data>标签中指定了数据格式,只有满足该action,并且数据满足格式满足data中指定的这个intent-filter才会通过,才会启动该activity。

TECH_DISCOVERED

        如果没有activity处理第一种intent,intent的action便为第二种。如上面的,假如数据格式不匹配,而手机中没有应用能处理action为ndef_discovered时的intent,那么action的intent便会变为tech_discovered。

        匹配该种Intent时,必须创建一个xml文件(该xml文件放置在res/xml文件夹中)用来指定当前activity所支持的nfc技术,这些技术名称写在tech-list集合中。在清单文件中,通过meta-data将这份xml引入。其中常用的技术及tech-list格式如下:

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

tech-list说明

        文档原文如下:
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().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(). 
        一份xml文件中可以有多个<tech-list>,每一个<tech-list>可以指定多个<tech>标签。假设设备支持的tech为集合A,只要有一个<tech-list>为集合A的子集,那么第二个intent便会匹配成功。否则不会。 

        如北京一卡通的tech为:android.nfc.tech.IsoDep与android.nfc.tech.NfcA。那么xml如下的话:

<tech-list><tech>android.nfc.tech.IsoDep</tech></tech-list><tech-list><tech>android.nfc.tech.NfcV</tech></tech-list><tech-list><tech>android.nfc.tech.NfcF</tech></tech-list>
该intent便会匹配成功。将第一个<tech-list>换成
<tech-list><tech>android.nfc.tech.IsoDep</tech><tech>android.nfc.tech.NfcA</tech></tech-list>
也会匹配成功。但如果将第一个换成:
<tech-list><tech>android.nfc.tech.IsoDep</tech><tech>android.nfc.tech.NfcB</tech></tech-list>
        那么第二个intent便不会匹配成功,因为NfcB并不在设备支持的集合中,所以该xml文件中没有一个<tech-list>是一卡通支持的子集。

示例

        其中的nfc_tech_filter便是上面的<tech-list>所在的xml文件。

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

TAG_DISCOVERED

        如果经过前两种仍没有找到activity匹配上,那么intent的action便会降为tag_discovered。配置如下:
<intent-filter>    <action android:name="android.nfc.action.TAG_DISCOVERED"/></intent-filter>
        但通过该action匹配上的可能很少,并且即使匹配上了,在处理nfc设备中的数据时也不方便。所以,不建议配置该filter

获取数据

        当该activity是因为nfc的intent被启动时, 可以从intent中获取这个设备的信息。intent含有如下的extra信息:
        1,EXTRA_TAG(必需的):一个代表着当前设备的Tag对象。
        2,EXTRA_NDEF_MESSAGES(可选的):从设备中解析的一系列的NDEF message。当intent的action为ACTION_NDEF_DISCOVERED,该extra便是必需的,一定有的。
        
3,EXTRA_ID(可选的):设备的id(low_level id)。

ndef格式

        当action为ndef_discovered时,系统会自动将nfc设备所存储的信息封装成NdefMessage对象,而一个NdefMessage中含有若干个NdefRecord对象。获取代码如下: 

if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {//判断是不是ndef_discovered            NdefMessage[] extra = (NdefMessage[]) getIntent().getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);            NdefRecord[] records = extra[0].getRecords();            Log.e(TAG, "读取到有数据:"+new String(records[0].getPayload()));
       虽然说获取到的NdefMessage数组大部分时候只有一个元素,但为了兼容以后的发展,故这里将NdefMessage数组封装到intent中了。

        由于一个message在可能含有多个record对象,所以nfc设备中的数据信息并不单单存储到第一个record中。

其余

分两步:

        第一步,获取tag对象:

Tag extra = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG);
        第二步:根据nfc设备支持的技术,将tag转换成相应的对象。如一卡通支持android.nfc.tech.IsoDep,转换如下:
IsoDep dep = IsoDep.get(extra);

这些类都在android.nfc.tech包中。而且所有的都是通过静态方法get()获取相应的对象。

NdefRecord

        NdefRecord应该包含如下信息:
        1,3-bit的tnf(Type Name Format):用于表明该如何解析可变长度的type字段。
        2,可变长度的type:描述该条record的type。
        3,可变长度的id:当前record的id。这个字段用的不多,但如果你需要当前tag的唯一标识,可以通过这个id进行创造。
        4,可变长度的负载(payload):程序需要读写的真实数据。由于一个message可以包含多个record,所以全部的负载并不仅仅包含在第一个record中。

示例

@Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);        if (adapter == null)            Toast.makeText(MainActivity.this, "nfc不支持", Toast.LENGTH_SHORT).show();        if (adapter != null && !adapter.isEnabled()) {            Toast.makeText(MainActivity.this, "nfc未打开", Toast.LENGTH_SHORT).show();        }        onNewIntent(getIntent());    }    @Override    protected void onNewIntent(Intent intent) {        super.onNewIntent(intent);        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {            NdefMessage[] extra = (NdefMessage[]) intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);            NdefRecord[] records = extra[0].getRecords();            Log.e(TAG, "读取到有数据:" + new String(records[0].getPayload()));        } else if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) {            Tag extra = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);            //android.nfc.tech.IsoDep    android.nfc.tech.NfcA            String[] list = extra.getTechList();            for (String tech : list) {                Log.e(TAG, tech);//输出当前nfc设备支持的tech            }            final IsoDep dep = IsoDep.get(extra);            if (dep != null) {                Toast.makeText(MainActivity.this, "iso dep", Toast.LENGTH_SHORT).show();            }        }    }
其清单文件intent-filter为:
<intent-filter>                <action android:name="android.nfc.action.NDEF_DISCOVERED"/>                <category android:name="android.intent.category.DEFAULT"/>            </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" />
其中的nfc_tech_filter如下:
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"><tech-list><tech>android.nfc.tech.IsoDep</tech></tech-list><tech-list><tech>android.nfc.tech.NfcV</tech></tech-list><tech-list><tech>android.nfc.tech.NfcF</tech></tech-list></resources>








0 0