Android NFC 学习笔记

4,085 阅读12分钟

NFC定义

NFC(Near Field Communication):近距离无线通讯技术。可以用于NFC标签与Android设备或两台Android设备间进行小型数据交换、访问内容与服务等。
NFC(近场通讯)是一系列短距离无线技术,一般需要4cm或者更短去初始化连接。NFC工作频率为13.65 兆赫兹,通信速率为106 kbit/秒到 848kbit/秒。

NFC的工作模式

由于NFC通信总是由一个发起者和一个接收者组成,发起者会主动发送电磁场,可以为被动接收者提供电源。正是由于被动式接收者可以通过发起者提供电源,因此接收者可以有非常简单的形式。NFC功能的工作模式可概括为三种:

  • 读卡器模式:该模式的本质为通过支持NFC的电子设备从带有NFC芯片的标签、物品等媒介中读写信息。NFC标签工作原理为:其不需要外部供电,当支持NFC的设备向标签读写数据时,支持NFC的设备会发送某种磁场,该磁场会主动向NFC标签供电,从而顺利完成读写工作。

  • 点对点模式:该模式是用于不同NFC设备之间进行数据交换,其有效距离一般不能超过4厘米,传输建立速度与传输速率比红外和蓝牙技术快很多。该模式下NFC进行传输时,通常还会使用到androidBeam技术进行传输数据,当使用androidBeam传输数据的两部设备不再限于4厘米之内。

  • 仿真卡模式。 其本质为将支持NFC功能的电子设备,如NFC手机,当作借书卡、公交卡、银行卡、门禁卡等IC卡使用。其实现的基本原理是将IC卡中信息凭证封装成数据包并存储在支持NFC的设备中。此外除了支持NFC的设备,刷电子设备处还需要NFC射频器,通过接收NFC射频器发送的信号与一系列验证之后,电子设备将IC卡中相应信息传入射频器中,最终从射频器传输到射频器所连接的电脑终端,完成相应处理操作。

Android.NFC

Android对NFC的支持主要在 android.nfc 和android.nfc.tech 两个包中。

1.android.nfc

  • NfcManager: 用于管理Android设备中指出的所有NFC Adapter,因为大多安卓设备只支持一个NFC Adapter的原因,所有在这种情况下可以直接使用getDefaultAapater 获取系统支持的Adapter。

  • NfcAdapter: 为一个NFC Adapter 对象,用于定义一个Intent使系统在检测到NFC Tag时通知你定义的Activity,并提供用来注册forground tag 消息发送的方法等。

  • NdefMessage:描述NDEF格式的信息,实际上我们写入NFC标签的就是NdefMessage对象。

  • NdefRecord:描述NDEF信息的一个信息段,一个NdefMessage可能包含一个或者多个NdefRecord。

  • Tag 代表一个被动式Tag对象,可以代表一个标签,卡片,钥匙扣等。当Android设备检测到一个Tag时,会创建一个Tag对象,将其放在Intent对象,然后发送到相应的Activity。

2.droid.nfc.tech

  • android.nfc.tech: 包中则定义了可以对Tag进行的读写操作的类,这些类按照其使用的技术类型可以分成不同的类如:NfcA, NfcB, NfcF,以及MifareClassic 等。

标签中的数据格式

标签的复杂度各有不同。简单标签仅提供读取和写入语义,有时可使用一次性可编程区域将卡片设置为只读。较复杂的标签可提供数学运算,还可使用加密硬件来验证对扇区的访问权限。最为复杂的标签可包含操作环境,允许与针对标签执行的代码进行复杂的互动。存储在标签中的数据也可以采用多种格式编写,但许多 Android 框架 API 都基于名为 NDEF(NFC 数据交换格式)的 NFC Forum 标准。

  • NDEF数据格式:一条NDEF消息,可能具有多条NDEF记录。当 Android 设备扫描包含 NDEF 格式数据的 NFC 标签时,它会解析该消息并尝试确定数据的 MIME 类型 或 起标识作用的 URI。为此,系统需要读取记录头(NdefMessage 中的第一条 NdefRecord如下图中的0字节数据),以确定如何解读整个 NDEF 消息。NDEF记录格式如下图

image.png

1.记录头(字节[0])

TNF : 类型名称格式字段。取值如下图:

image.png

IL:ID长度字段

IL标志指示ID长度字段是否存在。如果将其设置为0,则ID长度字段将在记录中省略。

SR:短记录位

如果PAYLOAD LENGTH字段为1字节(8位/ 0-255)或更小,则SR标志设置为1。这允许更紧凑的记录。

CF:块标志

CF标志指示这是第一个记录块还是中间记录块。

ME:消息结束

ME标志指示这是否是消息中的最后一条记录。

MB:消息开始

MB标志指示这是否是NDEF消息的开始。

2.type域 、id域、 palyload域

type :可变长度类型,类型域,介绍了记录的类型

id :可变长度ID,记录的唯一标识符。此字段并不经常使用,但如果您需要对标签进行唯一标识,则可为其创建 ID

palyload: 您要读取或写入的实际数据负载。

3.解析示例

照NDEF消息格式来解析下列Hex串

D1 02 1F 53 70 91 01 0E 54 02 

65 6E 68 65 6C 6C 6F 20 77 6F 

72 6C 64 51 01 09 55 01 73 69 

6E 61 2E 63 6F 6D

解析过程:

Byte 0: D1

    0xD1 = 11010001B,对应的标志位如下:

    MB(Message Begin) = 1,说明这是首记录

    ME(Message End) = 1,说明这是末记录(说明消息只有一个记录)

    CF(Chunk Flag) = 0,说明记录未被切块

    SR(Short Record) = 1,说明这是短记录,PAYLOAD_LENGTH 占1字节

    IL(ID_LENGTH field is present) = 0,说明没有 ID_LENGTH 和 ID 域

    TNF(Type Name Format) = 1,说明TYPE类型是 NFC Forum well-known type [NFC RTD]

image.png

因为SR=1且IL=0,所以NDEF记录可简化为如下结构

image.png

Byte 1: 02

    TYPE_LENGTH = 2,TYPE域长度为2

Byte 2: 1F

    PAYLOAD_LENGTH = 0x1F,Payload域长度为31

Byte 3-4: 53 70

    TYPE = "Sp",因为TYPE域长度为2,所以此处TYPE取2个字节

此后全是PAYLOAD域的内容,因为TYPE="Sp",所以需要使用智能海报的记录类型(Smart Poster Record Type Definition)来解析。智能海报的记录类型其实也是一个NDEF消息。NDEF记录的PAYLOAD域可以存放任何东西,包括嵌套NDEF消息。

智能海报的内容可以包括标题、URI、动作、图标、大小、类型等记录,其中URI记录是必须有的,其他的可选,次序无所谓。

   1)现在将智能海报的内容贴过来,如下

    91 01 0E 54 02 65 6E 68 65 6C 

    6C 6F 20 77 6F 72 6C 64 51 01 

    09 55 01 73 69 6E 61 2E 63 6F 

    6D

开始解析智能海报

Byte 0: 91

    0x91 = 10010001B,对应的标志位如下:

    MB = 1,说明这是首记录

    ME = 0,说明后面还有记录

    CF = 0,说明记录未被切块

    SR = 1,说明这是短记录,PAYLOAD_LENGTH 占1字节

    IL = 0,说明没有 ID_LENGTH 和 ID 域

    TNF = 1,说明TYPE类型是 NFC Forum well-known type

Byte 1: 01 TYPE_LENGTH = 1,TYPE域长度为1

Byte 2: 0E  PAYLOAD_LENGTH = 0x0E,Payload域长度为14

Byte 3: 54 TYPE = "T",说明是文本记录类型,适用“Text Record Type Definition”

Byte 4-17: 02 65 6E 68 65 6C 6C 6F 20 77 6F 72 6C 64  这是文本记录的内容

下面对“02 65 6E 68 65 6C 6C 6F 20 77 6F 72 6C 64”按照下面文本记录格式解码。

image.png

image.png

0x02 = 0 0 000010 B 语言长度及编码格式

000010 LANA 长度为2

0 = RFU

0 = UTF-8

LANA = 65 6E = en = US-ASCII

再按照US-ASCII 解析 68 65 6C 6C 6F 20 77 6F 72 6C 64 = hello world

至此,对海报内容的第一条NDEF记录解码完成,接下来是对剩余内容解码。

“51 01 09 55 01 73 69 6E 61 2E 63 6F 6D”,同样也是按照NDEF记录来解码。

Byte 0: 51

    0x91 = 01010001B,对应的标志位如下:

    MB = 0,说明这不是首记录

    ME = 1,说明这是末记录

    CF = 0,说明记录未被切块

    SR = 1,说明这是短记录,PAYLOAD_LENGTH 占1字节

    IL = 0,说明没有 ID_LENGTH 和 ID 域

    TNF = 1,说明TYPE类型是 NFC Forum well-known type

Byte 1: 01 TYPE_LENGTH = 1,TYPE域长度为1

Byte 2: 09 PAYLOAD_LENGTH = 0x09,Payload域长度为9

Byte 3: 55 TYPE = "U",说明是URI记录类型,适用“URI Record Type Definition”

Byte 4-12:  01 73 69 6E 61 2E 63 6F 6D 这是URI记录的内容

URI记录内容格式如下:

image.png

image.png

image.png

image.png

Byte 0: 01 由上表可知,对应的协议是 http://www

73 69 6E 61 2E 63 6F 6D 转 UTF-8string = sina.com

至此解析完毕

  • 非NDEF数据:若想支持其他的不包含NDEF数据类型的标签,要存任意的数据,可以自行定义格式,编写自己的跟该标签通信的协议栈。这些数据格式本质也是普通的字节流,其数据含义可由开发人员自行定义。非NDEF数据格式的解析 下方 NFC标签调度分发 有详细讲解

NFC标签调度分发

当标签调度系统创建完用于封装 NFC 标签及其标识信息的 Intent 后,它会将该 Intent 发送给感兴趣的应用,由这些应用对其进行过滤。如果有多个应用可处理该 Intent,系统会显示 Activity 选择器,供用户选择要使用的 Activity。标签调度系统定义了三种 Intent,按优先级从高到低列出如下:

  1. ACTION_NDEF_DISCOVERED:如果扫描到包含 NDEF 负载的标签,并且可识别其类型,则使用此 Intent 启动 Activity。这是优先级最高的 Intent,标签调度系统会尽可能尝试使用此 Intent 启动 Activity,在行不通时才会尝试使用其他 Intent。 适用于上文描述的使用标准NDEF格式的情况。

  2. ACTION_TECH_DISCOVERED:如果没有登记要处理 ACTION_NDEF_DISCOVERED Intent 的 Activity,则标签调度系统会尝试使用此 Intent 来启动应用。此外,如果扫描到的标签包含无法映射到 MIME 类型或 URI 的 NDEF 数据,或者该标签不包含 NDEF 数据,但它使用了已知的标签技术,那么也会直接启动此 Intent。 适用于使用自定义 非标准NDEF数据格式的情况。

  3. ACTION_TAG_DISCOVERED:如果没有处理 ACTION_NDEF_DISCOVERED 或者 ACTION_TECH_DISCOVERED Intent 的 Activity,则使用此 Intent 启动 Activity。

您通常需要过滤 NDEF Intent,以最有力地控制应用在何时启动。如果没有应用过滤 NDEF,或者负载不是 NDEF,TECH Intent 会取代 NDEFTAG 通常因过于笼统而不适合过滤。许多应用会在过滤 TAG 前过滤 NDEF 或 TECH,导致您的应用启动的概率会比较低。过滤 TAG 是应用在没有其他应用来处理 NDEF 或 TECH Intent 的情况下的最后一道保险。

ACTION_NDEF_DISCOVERED 不一定每次都可用,您可以根据需要回退到另外两种 Intent。如果您可以控制标签和写入数据的类型,建议您使用 NDEF 来设置标签的格式。

实现NFC的基本步骤

1.Manifest配置

//NFC权限
<uses-permission android:name="android.permission.NFC" />
//支持的最低sdk版本
<uses-sdk android:minSdkVersion="10"/>
//声明只有带NFC的手机才可以下载
<uses-feature android:name="android.hardware.nfc" android:required="true" />

2.intent-filter过滤器声明

  • ACTION_NDEF_DISCOVERED

以下示例展示了如何过滤 MIME 类型为 text/plain 的 ACTION_NDEF_DISCOVERED Intent:

  <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain" />
    </intent-filter>
    

以下示例展示了如何过滤采用 https://developer.android.com/index.html 形式的 URI。

<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" />
    </intent-filter>
  • ACTION_TECH_DISCOVERED

如果您的 Activity 过滤 ACTION_TECH_DISCOVERED Intent,您必须创建一个 XML 资源文件,用它在 tech-list 集内指定您的 Activity 所支持的技术。如果 tech-list 集是标签所支持的技术的子集,则您的 Activity 会被视为一个匹配项。

以下示例展示了如何过滤  ACTION_TECH_DISCOVERED Intent:

<activity
    android:name=".mvp.activity.NfcActivity"
    android:launchMode="singleTask"
    android:screenOrientation="portrait">
    <meta-data
        android:name="android.nfc.action.TECH_DISCOVERED"
        android:resource="@xml/nfc_tech_filter" />
</activity>

nfc_tech_filter 以下示例定义了所有技术。您可以选择自己需要的技术

 <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 集。每个 tech-list 集都是独立的;如果任意一个 tech-list 集是由 getTechList() 返回的技术的子集,则您的 Activity 会被视为一个匹配项。这为匹配技术提供了 AND 和 OR 语义。以下示例展示了如何与支持 NfcA 和 Ndef 技术的标签或者支持 NfcB 和 Ndef 技术的标签相匹配:

<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>
        <tech-list>
            <tech>android.nfc.tech.NfcB</tech>
            <tech>android.nfc.tech.Ndef</tech>
        </tech-list>
    </resources>
    

从 Intent 中获取信息

如果某个 Activity 由于 NFC Intent 而启动,您可以从该 Intent 中获取有关扫描到的 NFC 标签的信息。Intent 可以包含以下 extra,具体取决于扫描到的标签:

  • EXTRA_TAG(必需):一个 Tag 对象,表示扫描到的标签。

  • EXTRA_NDEF_MESSAGES(可选):从标签中解析出的一组 NDEF 消息。此 extra 对于 ACTION_NDEF_DISCOVERED Intent 而言是必需的。

  • EXTRA_ID(可选):标签的低级别 ID。

要获取这些 extra,请检查您的 Activity 是不是使用某个 NFC Intent 启动的,以确保已扫描到标签,然后获取 Intent 的 extra。以下示例展示了如何检查 ACTION_NDEF_DISCOVERED Intent 并从 Intent extra 获取 NDEF 消息

 @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        ...
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
            Parcelable[] rawMessages =
                intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
            if (rawMessages != null) {
                NdefMessage[] messages = new NdefMessage[rawMessages.length];
                for (int i = 0; i < rawMessages.length; i++) {
                    messages[i] = (NdefMessage) rawMessages[i];
                }
                // Process the messages array.
                ...
            }
        }
    }

对于使用自定义的非NDEF数据格式 您可以检查 ACTION_TECH_DISCOVERED,并从 Intent 中获取 Tag 对象

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

参考文档
blog.csdn.net/qq_41522183… www.blogjava.net/ZouYonghui/…