NFC in Android: Grundlagen und Integrationsmuster
NFC erlaubt Android-Apps die kontaktlose Kommunikation mit Tags in Reichweite. Hier lernst du die wichtigsten Integrationsmuster kennen.
NFC (Near Field Communication) ist eine Funktechnologie, die kontaktlose Datenübertragung auf wenigen Zentimetern ermöglicht. In Android-Apps begegnet dir NFC beim Auslesen von Zugangskarten, beim Bezahlen per Smartphone und beim schnellen Koppeln von Geräten – kurz überall dort, wo physische Nähe allein als sicherer Trigger genügt. Für dich als Android-Entwicklerin oder -Entwickler bedeutet das: hardwarenahe APIs, strikte Berechtigungsmodelle und ein durchdachtes Intent-Routing.
Was ist das?
NFC ist ein Kurzstrecken-Funkstandard, der auf dem RFID-Protokoll basiert und bei 13,56 MHz arbeitet. Die Reichweite beträgt typischerweise maximal vier Zentimeter – diese physische Einschränkung ist gleichzeitig ein Sicherheitsmerkmal: Ein Angreifer muss Gerät und Tag fast berühren, um Daten abzufangen.
Im Android-Ökosystem taucht NFC in drei Hauptszenarien auf:
- Tag-Lesen und -Schreiben: Deine App reagiert auf passive NFC-Tags, etwa Aufkleber oder Karten, liest Produktdaten, öffnet URLs oder speichert Konfigurationen.
- Peer-to-Peer-Kommunikation: Zwei Android-Geräte tauschen direkt Daten aus. Dieses Muster ist seit dem Ende von Android Beam zugunsten von Bluetooth und Wi-Fi Direct seltener geworden.
- Host-based Card Emulation (HCE): Das Android-Gerät täuscht eine NFC-Karte vor – das ist die Grundlage für kontaktloses Bezahlen per Google Pay.
Im Roadmap-Kontext steht NFC stellvertretend für alle hardwarenahen Plattform-APIs, die Permission-Modelle, Intent-Filter und ein bewusstes Lifecycle-Handling erfordern. Wer NFC versteht, überträgt dieses Denkmuster auf andere Gerätefähigkeiten wie Bluetooth LE oder USB.
Wie funktioniert es?
Android leitet ein erkanntes NFC-Signal über das Tag-Dispatch-System an Apps weiter. Das System prüft dabei drei Intent-Filter in absteigender Priorität:
ACTION_NDEF_DISCOVERED– der speziellste Intent, nur für Tags mit NDEF-Daten des angegebenen MIME-Typs oder URI-Schemas.ACTION_TECH_DISCOVERED– greift, wenn kein NDEF-Handler registriert ist; du gibst explizit an, welche NFC-Technologien deine App versteht, etwaNfcA,NfcBoderIsoDep.ACTION_TAG_DISCOVERED– allgemeiner Fallback für alle Tags, die von den oberen zwei Filtern nicht abgedeckt werden.
Der Einstiegspunkt im Code ist der NfcAdapter. Er liefert den aktuellen Adapter des Geräts oder null, wenn kein NFC-Chip vorhanden ist:
val nfcAdapter = NfcAdapter.getDefaultAdapter(context)
if (nfcAdapter == null || !nfcAdapter.isEnabled) {
// NFC nicht verfügbar oder im System deaktiviert
}
Um einen NDEF-Tag zu lesen, dekodierst du das Parcelable-Extra aus dem eingehenden Intent:
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
val rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)
val ndefMessage = rawMessages?.firstOrNull() as? NdefMessage
ndefMessage?.records?.forEach { record ->
val payload = String(record.payload)
// Nutzdaten verarbeiten
}
}
}
Damit Android eingehende Intents korrekt verteilt, musst du deine Activity im Manifest mit den passenden <intent-filter>-Einträgen deklarieren und die Berechtigung android.permission.NFC eintragen.
In der Praxis
Foreground Dispatch als Standardmuster
Sobald deine Activity im Vordergrund ist und NFC-Tags sofort verarbeiten soll, nutzt du Foreground Dispatch. Damit erhält deine Activity den NFC-Intent, bevor das System andere installierte Apps oder den Startbildschirm befragen kann:
// In onResume()
val pendingIntent = PendingIntent.getActivity(
this, 0,
Intent(this, javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
PendingIntent.FLAG_MUTABLE
)
nfcAdapter?.enableForegroundDispatch(this, pendingIntent, null, null)
// In onPause()
nfcAdapter?.disableForegroundDispatch(this)
Typische Stolperfalle: onPause-Cleanup vergessen
Der häufigste Fehler ist, disableForegroundDispatch in onPause wegzulassen. Die Activity empfängt dann Intents, auch wenn sie gar nicht mehr sichtbar ist, was zu Abstürzen in Hintergrundoperationen führen kann. Beachte außerdem, dass enableForegroundDispatch ausschließlich vom Main-Thread aus aufgerufen werden darf – ein Aufruf aus einem Coroutine-Dispatcher mit Dispatchers.IO führt zu einer IllegalStateException.
Berechtigungen und Manifest-Deklaration
android.permission.NFC ist eine normale Berechtigung (nicht als gefährlich eingestuft), die ausschließlich im Manifest deklariert wird. Du benötigst keinen requestPermissions-Dialog zur Laufzeit. Fehlt der Eintrag jedoch, wirft der NfcAdapter beim ersten Zugriff eine SecurityException.
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="false" />
android:required="false" sorgt dafür, dass deine App im Play Store auch auf Geräten ohne NFC-Chip angeboten wird – wichtig, wenn NFC ein optionales Feature ist.
Jetpack und Compose
In Compose-Apps wickelst du die NfcAdapter-Logik am besten in ein ViewModel und gibst erkannte Daten über einen StateFlow nach oben weiter. Das Intent-Handling landet weiterhin in der zugrunde liegenden Activity; von dort übergibst du den Intent an das ViewModel. Diese Trennung hält deine Composables frei von Plattform-Abhängigkeiten und erleichtert Unit-Tests der Dekodierlogik.
Fazit
NFC-Integration in Android erfordert Sorgfalt im Lifecycle-Management: Der Foreground-Dispatch-Mechanismus muss symmetrisch in onResume und onPause gesetzt werden, und der NfcAdapter muss stets auf null geprüft werden, bevor du ihn verwendest. Prüfe dein Verständnis, indem du eine kleine App baust, die den Text eines NDEF-Tags ausliest und als Snackbar anzeigt – nutze dafür einen physischen NFC-Aufkleber oder den NFC-Tag-Emulator im Android-Emulator. Schreibe anschließend einen Unit-Test für deine Payload-Dekodierlogik und sieh im Code-Review nach, ob onPause-Cleanup konsequent im Team eingehalten wird.