本文档介绍了高级 NFC 主题,例如如何使用各种标签技术、 写入 NFC 标签以及前台调度,这使得前台的应用可以 即使其他应用过滤相同的 intent,也会处理这些 intent。
使用支持的标签技术
在使用 NFC 标签和 Android 设备时,读取文件中
标签数据是 NDEF。当设备扫描包含 NDEF 数据的标签时,Android 会提供支持
在解析消息并在 NdefMessage
中传递它时,
不过,在某些情况下,您扫描的代码不包含
NDEF 数据,或者 NDEF 数据无法映射到 MIME 类型或 URI 时。
在这些情况下,您需要直接与代码进行通信,并使用
(以原始字节为单位)。Android 通过
android.nfc.tech
软件包(如表 1 中所述)。您可以
使用 getTechList()
方法确定技术
创建相应的TagTechnology
对象与 android.nfc.tech
提供的某个类相关联
类 | 说明 |
---|---|
TagTechnology |
这是所有标签技术类都必须实现的接口。 |
NfcA |
提供对 NFC-A (ISO 14443-3A) 属性和 I/O 操作的访问权限。 |
NfcB |
提供对 NFC-B (ISO 14443-3B) 属性和 I/O 操作的访问权限。 |
NfcF |
提供对 NFC-F (JIS 6319-4) 属性和 I/O 操作的访问权限。 |
NfcV |
提供对 NFC-V (ISO 15693) 属性和 I/O 操作的访问权限。 |
IsoDep |
提供对 ISO-DEP (ISO 14443-4) 属性和 I/O 操作的访问权限。 |
Ndef |
提供对以下格式的 NFC 标签上 NDEF 数据和操作的访问权限: NDEF。 |
NdefFormatable |
为可设置为 NDEF 格式的标签提供格式化操作。 |
Android 设备不需要支持以下标签技术。
类 | 说明 |
---|---|
MifareClassic |
提供对 MIFARE Classic 属性和 I/O 操作的访问权限(如果此 Android 设备) 支持 MIFARE。 |
MifareUltralight |
提供对 MIFARE Ultralight 属性和 I/O 操作的访问权限(如果此 Android 设备支持 MIFARE。 |
将标签技术和 ACTION_TECH_DISCOVERED Intent 结合使用
如果设备扫描的标签包含 NDEF 数据,但无法映射到 MIME 或 URI,
标签调度系统尝试使用 ACTION_TECH_DISCOVERED
启动 activity
intent。ACTION_TECH_DISCOVERED
也可用于标记
包含非 NDEF 数据的数据。有了此后备广告,您就可以处理代码中的数据
。使用
标签技术的说明如下:
- 过滤指定
ACTION_TECH_DISCOVERED
intent 您要处理的代码技术请参阅过滤 NFC intent。一般情况下,标签调度系统会在收到 NDEF 消息时尝试启动ACTION_TECH_DISCOVERED
intent 无法映射到 MIME 类型或 URI,或者扫描的标记不包含 NDEF 数据。对于 如需详细了解如何确定,请参阅代码调度系统。 - 当应用收到 intent 时,从
Tag
intent:Kotlin
var tagFromIntent: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
Java
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
- 通过调用以下其中一个方法获取
TagTechnology
的实例:android.nfc.tech
软件包中类的get
工厂方法。您可以 通过在调用get
工厂方法之前调用getTechList()
,枚举支持的标签技术。例如,如需获取 (从Tag
获取MifareUltralight
),请执行以下操作:Kotlin
MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG))
Java
MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG));
对标签执行读写操作
对 NFC 标签执行读写操作涉及从 intent 中获取标签, 以便与代码进行通信您必须定义自己的协议栈才能读取和写入数据 添加到代码中。不过请注意,在工作时,您仍然可以读取和写入 NDEF 数据 直接使用 标记。具体取决于您要如何设计其结构。通过 以下示例展示了如何使用 MIFARE Ultralight 标签。
Kotlin
package com.example.android.nfc import android.nfc.Tag import android.nfc.tech.MifareUltralight import java.io.IOException import java.nio.charset.Charset class MifareUltralightTagTester { fun writeTag(tag: Tag, tagText: String) { MifareUltralight.get(tag)?.use { ultralight -> ultralight.connect() Charset.forName("US-ASCII").also { usAscii -> ultralight.writePage(4, "abcd".toByteArray(usAscii)) ultralight.writePage(5, "efgh".toByteArray(usAscii)) ultralight.writePage(6, "ijkl".toByteArray(usAscii)) ultralight.writePage(7, "mnop".toByteArray(usAscii)) } } } fun readTag(tag: Tag): String? { return MifareUltralight.get(tag)?.use { mifare -> mifare.connect() val payload = mifare.readPages(4) String(payload, Charset.forName("US-ASCII")) } } }
Java
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 writing 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 reading MifareUltralight message...", e); } finally { if (mifare != null) { try { mifare.close(); } catch (IOException e) { Log.e(TAG, "Error closing tag...", e); } } } return null; } }
使用前台调度系统
前台调度系统允许 activity 拦截 intent 和声明 相较于处理同一 intent 的其他 activity 的优先级。使用此系统涉及 构造一些数据结构,以便 Android 系统能够发送相应的 intent。要启用前台调度系统,请执行以下操作:
- 在您的 activity 的
onCreate()
方法中添加以下代码: <ph type="x-smartling-placeholder">- </ph>
- 创建一个可变的
PendingIntent
对象,以便 Android 系统可以对其进行填充 并在扫描时包含代码的详细信息Kotlin
val intent = Intent(this, javaClass).apply { addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) } var pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE)
Java
PendingIntent pendingIntent = PendingIntent.getActivity( this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_MUTABLE);
- 声明 Intent 过滤器,以处理您要拦截的 Intent。前景
调度系统会根据以下情况检查指定的 intent 过滤器:
设备会扫描标签如果匹配,那么应用会处理该 Intent。如果是
如果不匹配,前台调度系统会回退到 intent 调度系统。
指定由 intent 过滤器和技术过滤器组成的
null
数组, 您要过滤出所有回退到TAG_DISCOVERED
的代码 intent。以下代码段会处理NDEF_DISCOVERED
的所有 MIME 类型。您 应该只处理您需要的代码Kotlin
val ndef = IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED).apply { try { addDataType("*/*") /* Handles all MIME based dispatches. You should specify only the ones that you need. */ } catch (e: IntentFilter.MalformedMimeTypeException) { throw RuntimeException("fail", e) } } intentFiltersArray = arrayOf(ndef)
Java
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, };
- 设置应用要处理的一组标签技术。调用
Object.class.getName()
方法获取您要使用的技术的类 支持。Kotlin
techListsArray = arrayOf(arrayOf<String>(NfcF::class.java.name))
Java
techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
- 创建一个可变的
- 替换以下 activity 生命周期回调,并添加逻辑以启用和停用
activity 丢失时前台调度 (
onPause()
) 并重新获得 (onResume()
) 专注模式。必须从以下来源调用enableForegroundDispatch()
: 主线程;仅当 activity 位于前台时(在onResume()
中调用可保证这一点)。您还需要实现onNewIntent
回调,以处理来自已扫描 NFC 的数据 标记前面。
Kotlin
public override fun onPause() { super.onPause() adapter.disableForegroundDispatch(this) } public override fun onResume() { super.onResume() adapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray) } public override fun onNewIntent(intent: Intent) { val tagFromIntent: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG) // do something with tagFromIntent }
Java
public void onPause() { super.onPause(); adapter.disableForegroundDispatch(this); } public void onResume() { super.onResume(); adapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray); } public void onNewIntent(Intent intent) { Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); // do something with tagFromIntent }