Kitkat 是否支持 NFC NDEF?

问题描述

我正在尝试使用在 KitKat 上运行的 MobiPrint 设备读取 NFC NDEF 消息。

我按照 Docs 中的说明进行操作,但我似乎无法让它在 Kitkat 上运行。

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.pos">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.NFC" />

    <uses-feature android:name="android.hardware.nfc" android:required="true"/>

    <application
        android:name=".App"
        android:allowBackup="true"
        android:fullBackupContent="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Pos">
        <activity
            android:name=".ui.views.main.MainActivity"
            android:theme="@style/Theme.Pos.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
   
            <intent-filter>
                <action android:name="android.nfc.action.NDEF_disCOVERED" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="text/plain" />
            </intent-filter>
        </activity>
    </application>
</manifest>

MainActivity.kt

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    private var adapter: NfcAdapter? = null
    private var pendingIntent: PendingIntent? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        adapter = NfcAdapter.getDefaultAdapter(this)

        pendingIntent = PendingIntent.getActivity(this,Intent(this,javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),0)
    }

    override fun onResume() {
        super.onResume()
        adapter?.enableForegrounddispatch(this,pendingIntent,null,null)
    }

    override fun onPause() {
        super.onPause()
        adapter?.disableForegrounddispatch(this)
    }

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)

        val rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
        Log.d("NFC_NDEF_MESSAGES",rawMessages.toString())
        Log.d("NFC_NDEF_ACTION",intent.action.toString())
    }
}

Logcat 消息

带有 Kitkat 的 MobiPrint 设备:

NFC_NDEF_MESSAGES:null NFC_NDEF_ACTION:android.nfc.action.TAG_disCOVERED

S7 Active with Marshmallow:

NFC_NDEF_MESSAGES:[Landroid.os.Parcelable;@6405e69 NFC_NDEF_ACTION:android.nfc.action.NDEF_disCOVERED

Kitkat 不支持 NDEF 吗?

解决方法

恩智浦 MIFARE Classic 系列 NFC 卡不符合 NFC 论坛标准(该规范早于 NFC 标准)

因此某些硬件不支持 NXP MIFARE Classic 系列 NFC 卡,如果您查看 Android 文档 https://developer.android.com/reference/android/nfc/tech/MifareClassic

此类在 Android NFC 设备上的实现是可选的。如果没有实现,那么 MifareClassic 将永远不会被列在 Tag#getTechList 中。如果是枚举,则将支持所有 MifareClassic I/O 操作,也将支持 Ndef#MIFARE_CLASSIC NDEF 标签。在任一情况下,NfcA 也会在标签上枚举,因为所有 MIFARE Classic 标签也是 NfcA。

因此,对此类卡的支持在 Android 中是可选的,正如它所说,如果支持它,那么将在其之上支持更高级别的 NDEF 协议(但在您的情况下,MifareClassic 级别不支持它,然后顶层将不支持 NDEF)

这可能就是您收到 android.nfc.action.TAG_DISCOVERED 消息的原因,因为它来自低级 NfcA,这是 MifareClassic 与其他 NFC 规范卡共有的低级协议。

不幸的是,让 MobiPrint 设备读取 NFC 标签的解决方案是使用购买不同的 NFC 标签,这与 Android 版本无关(即使一些后来的 Android 硬件也不会读取它们),我建议使用 NFC 论坛规范类型标签。 接近匹配的是 NXP TAG 216 卡(NFC 论坛类型 2)或 NXP Desfire(NFC 论坛类型 4)

,

出于某种原因,它检测到它为 TAG_DISCOVERED(通常提示缺少 NDEF 格式)。即使没有检测到它为MifareUltralight,仍然可以将其用作NfcA,CC在第3页,数据区从第4页开始(无论是格式化还是读取格式)。当标签在更高版本的 Android 上可读时,这些绝对应该算作“可读”。

它所需要的只是一个处理 MifareUltralightNfcA 的 KitKat 实现。

无需检查 API 级别即可回退:

when (intent.action.toString()) {
    android.nfc.action.NDEF_DISCOVERED -> {
        /* handle NDEF messages (business as usual). */
    }
    android.nfc.action.TAG_DISCOVERED -> {

        /* Check if NDEF format is present and read it. */
        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
        MifareUltralight mifare = MifareUltralight.get(tag)
        mifare.connect()
        ...
        mifare.close()

        /* When MifareUltralight is null,try NfcA: */
        // NfcA nfcA = NfcA.get(tag)
    }
    else -> {
        print("unknown intent action")
    }
}

MifareUltralight 知道页面,NfcA 是相当低级的并且只知道 ByteArray 有效负载。
而不是建议不同的标签(这可能会触发相同的 {{1 }},只需要相应地处理),您可以改为提高 Intent 并完全停止支持 KitKat(minApiLevel 仍然可能为空标签触发)。我有 NTAG216 和至少一个物理 API 22 设备 - 看看它会实际触发哪个Intent,但没有时间。人们需要一个物理 API 19 设备来正确地重现这一点。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...