NFC-tags lezen met Android

Ben je benieuwd naar wat NFC is en hoe het kan worden geïntegreerd in je eigen Android-applicaties? Deze tutorial zal je snel introduceren in het onderwerp voordat je erin gaat duiken en je leert hoe je een eenvoudige NFC-readerapp kunt bouwen!


Wat is NFC?

NFC is de afkorting voor Near Field Communication. Het is de internationale standaard voor contactloze gegevensuitwisseling. In tegenstelling tot een groot aantal andere technologieën, zoals draadloze LAN en Bluetooth, is de maximale afstand van twee apparaten 10 cm. De ontwikkeling van de standaard begon in 2002 door NXP Semiconductors en Sony. Het NFC Forum, een consortium van meer dan 170 bedrijven en leden, waaronder Mastercard, NXP, Nokia, Samsung, Intel en Google, ontwerpt sinds 2004 nieuwe specificaties.

Er zijn verschillende mogelijkheden voor NFC-gebruik met mobiele apparaten; bijvoorbeeld papierloze tickets, toegangscontroles, girale betalingen en autosleutels. Met behulp van NFC-tags kunt u uw telefoon bedienen en instellingen wijzigen. Gegevens kunnen eenvoudig worden uitgewisseld door twee apparaten naast elkaar te houden.

In deze zelfstudie wil ik uitleggen hoe NFC met de Android SDK kan worden geïmplementeerd, welke valkuilen er zijn en wat u in gedachten moet houden. We zullen stap voor stap een app maken, die de inhoud kan lezen van NFC-tags die NDEF ondersteunen.


NFC Technologies

Er zijn verschillende NFC-tags die met een smartphone kunnen worden gelezen. Het spectrum varieert van eenvoudige stickers en sleutelringen tot complexe kaarten met geïntegreerde cryptografische hardware. Tags verschillen ook in hun chiptechnologie. Het belangrijkste is NDEF, dat wordt ondersteund door de meeste tags. In additie zou Mifare vermeld moeten worden, aangezien dit de meest gebruikte contactloze chiptechnologie wereldwijd is. Sommige tags kunnen worden gelezen en geschreven, terwijl andere alleen-lezen of gecodeerd zijn.

Alleen het NFC Data Exchange Format (NDEF) wordt besproken in deze tutorial.


NFC-ondersteuning toevoegen in een app

We beginnen met een nieuw project en een lege activiteit. Het is belangrijk om een ​​minimale SDK-versie van niveau 10 te selecteren, omdat NFC alleen wordt ondersteund na Android 2.3.3. Vergeet niet om uw eigen pakketnaam te kiezen. ik heb gekozen net.vrallev.android.nfc.demo, omdat vrallev.net het domein is van mijn website en het andere deel verwijst naar het onderwerp van deze applicatie.

 

De standaard lay-out gegenereerd door Eclipse is bijna voldoende voor ons. Ik heb alleen een ID aan de TextView toegevoegd en de tekst gewijzigd.

 

Om toegang te krijgen tot de NFC-hardware moet u toestemming vragen in het manifest. Als de app niet werkt zonder NFC, kunt u de voorwaarde opgeven met de tag uses-feature. Als NFC vereist is, kan de app niet worden geïnstalleerd op apparaten zonder deze en geeft Google Play uw app alleen weer aan gebruikers die een NFC-apparaat hebben.

  

De hoofdactiviteit mag alleen bestaan ​​uit de methode onCreate (). U kunt de hardware gebruiken via de klasse NfcAdapter. Het is belangrijk om te weten te komen of de NfcAdapter null is. In dit geval ondersteunt het Android-apparaat NFC niet.

 pakket net.vrallev.android.nfc.demo; import android.app.Activity; importeer android.nfc.NfcAdapter; import android.os.Bundle; import android.widget.TextView; import android.widget.Toast; / ** * Activiteit voor het lezen van gegevens van een NDEF-tag. * * @author Ralf Wondratschek * * / public class MainActivity breidt activiteit uit public static final String TAG = "NfcDemo"; privé TextView mTextView; private NfcAdapter mNfcAdapter; @Override protected void onCreate (Bundle savedInstanceState) super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); mTextView = (TextView) findViewById (R.id.textView_explanation); mNfcAdapter = NfcAdapter.getDefaultAdapter (this); if (mNfcAdapter == null) // Stop hier, we hebben absoluut NFC Toast.makeText nodig (dit: "Dit apparaat ondersteunt NFC niet.", Toast.LENGTH_LONG) .show (); af hebben(); terug te keren;  if (! mNfcAdapter.isEnabled ()) mTextView.setText ("NFC is uitgeschakeld.");  else mTextView.setText (R.string.explanation);  handleIntent (getIntent ());  private void handleIntent (Intent intent) // TODO: intent verwerken

Als we onze app nu starten, kunnen we de tekst zien of NFC is ingeschakeld of uitgeschakeld.

Hoe te filteren op NFC-tags

We hebben onze voorbeeldapp en willen een melding van het systeem ontvangen wanneer we een NFC-tag aan het apparaat koppelen. Zoals gewoonlijk gebruikt Android het Intent-systeem om tags aan de apps te leveren. Als meerdere apps de intentie aankunnen, wordt de activiteitskiezer weergegeven en kan de gebruiker beslissen welke app wordt geopend. Het openen van URL's of het delen van informatie wordt op dezelfde manier afgehandeld.


NFC Intent Filter

Er zijn drie verschillende filters voor tags:

  1. ACTION_NDEF_DISCOVERED
  2. ACTION_TECH_DISCOVERED
  3. ACTION_TAG_DISCOVERED

De lijst wordt gesorteerd van de hoogste tot de laagste prioriteit.

Wat gebeurt er wanneer een tag aan de smartphone is bevestigd? Als het systeem een ​​tag met NDEF-ondersteuning detecteert, wordt een Intent geactiveerd. Een ACTION_TECH_DISCOVERED De intentie wordt geactiveerd als er geen activiteit van een app is geregistreerd voor de NDEF Intent of als de tag geen ondersteuning biedt voor NDEF. Als er opnieuw geen app wordt gevonden voor de Intent of de chiptechnologie niet kon worden gedetecteerd, dan is ACTION_TAG_DISCOVERED De intentie is ontslagen. De volgende afbeelding toont het proces:


Samengevat betekent dit dat elke app na de intentie met de hoogste prioriteit moet filteren. In ons geval is dit de NDEF-intentie. We implementeren de ACTION_TECH_DISCOVERED De bedoeling om eerst het verschil tussen prioriteiten te benadrukken.


Tech Discovered Intent

We moeten de technologie specificeren waarin we geïnteresseerd zijn. Hiervoor maken we een submap met de naam xml in de res map. In deze map maken we het bestand nfc_tech_filter.xml, waarin we de technologieën specificeren.

    android.nfc.tech.Ndef    

Nu moeten we een IntentFilter maken in het manifest en de app wordt gestart wanneer we een tag koppelen.

           

Als er geen andere app voor deze Intent is geregistreerd, start onze Activiteit onmiddellijk. Op mijn apparaat zijn echter andere apps geïnstalleerd, zodat de activiteitskiezer wordt weergegeven.



NDEF Discovered Intent

Zoals ik eerder al zei, heeft de Tech Discovered Intent de op een na hoogste prioriteit. Aangezien onze app alleen NDEF ondersteunt, kunnen we in plaats daarvan de NDEF Discovered Intent gebruiken, die een hogere prioriteit heeft. We kunnen de technologielijst opnieuw verwijderen en de IntentFilter vervangen door de volgende.

     

Wanneer we de tag nu toevoegen, wordt de app op dezelfde manier gestart. Er is echter een verschil voor mij. De activiteitskiezer wordt niet weergegeven en de app wordt onmiddellijk gestart, omdat de NDEF-intentie een hogere prioriteit heeft en de andere apps alleen voor de lagere prioriteiten zijn geregistreerd. Dat is precies wat we willen.


Voorgrond verzending

Merk op dat er nog een probleem resteert. Wanneer onze app al is geopend en we de tag opnieuw toevoegen, wordt de app een tweede keer geopend in plaats van de tag rechtstreeks af te leveren. Dit is niet ons beoogde gedrag. U kunt het probleem omzeilen door een voorgrondreferentie te gebruiken.

In plaats van dat het systeem de Intent heeft gedistribueerd, kunt u uw activiteit registreren om de tag rechtstreeks te ontvangen. Dit is belangrijk voor een bepaalde workflow, waar het geen zin heeft om een ​​andere app te openen.

Ik heb de uitleg op de juiste plaatsen in de code ingevoegd.

 pakket net.vrallev.android.nfc.demo; import android.app.Activity; importeer android.app.PendingIntent; importeer android.content.Intent; importeer android.content.IntentFilter; importeer android.content.IntentFilter.MalformedMimeTypeException; importeer android.nfc.NfcAdapter; import android.os.Bundle; import android.widget.TextView; import android.widget.Toast; / ** * Activiteit voor het lezen van gegevens van een NDEF-tag. * * @author Ralf Wondratschek * * / public class MainActivity breidt Activiteit uit public static final String MIME_TEXT_PLAIN = "text / plain"; public static final String TAG = "NfcDemo"; privé TextView mTextView; private NfcAdapter mNfcAdapter; @Override protected void onCreate (Bundle savedInstanceState) super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); mTextView = (TextView) findViewById (R.id.textView_explanation); mNfcAdapter = NfcAdapter.getDefaultAdapter (this); if (mNfcAdapter == null) // Stop hier, we hebben absoluut NFC Toast.makeText nodig (dit: "Dit apparaat ondersteunt NFC niet.", Toast.LENGTH_LONG) .show (); af hebben(); terug te keren;  if (! mNfcAdapter.isEnabled ()) mTextView.setText ("NFC is uitgeschakeld.");  else mTextView.setText (R.string.explanation);  handleIntent (getIntent ());  @Override beschermde leegte onResume () super.onResume (); / ** * Het is belangrijk dat de activiteit op de voorgrond staat (hervat). Anders * wordt een IllegalStateException gegenereerd. * / setupForegroundDispatch (this, mNfcAdapter);  @Override protected void onPause () / ** * Noem dit voor onPause, anders wordt ook een IllegalArgumentException gegenereerd. * / stopForegroundDispatch (this, mNfcAdapter); super.onPause ();  @Override protected void onNewIntent (Intent intent) / ** * Deze methode wordt aangeroepen, wanneer een nieuwe Intent wordt gekoppeld aan het huidige activiteitsinstituut. * In plaats van een nieuwe activiteit te maken, wordt onNewIntent aangeroepen. Kijk voor meer informatie * bij de documentatie. * * In ons geval wordt deze methode aangeroepen, wanneer de gebruiker een tag aan het apparaat koppelt. * / handleIntent (opzet);  private void handleIntent (Intent opzet) // TODO: handle Intent / ** * @param activity De bijbehorende @link Activity vraagt ​​om de voorgrond verzending. * @param-adapter De @link NfcAdapter die wordt gebruikt voor de verzending op de voorgrond. * / public static void setupForegroundDispatch (laatste Activiteitsactiviteit, NfcAdapter-adapter) final Intent intent = new Intent (activity.getApplicationContext (), activity.getClass ()); intent.setFlags (Intent.FLAG_ACTIVITY_SINGLE_TOP); final PendingIntent pendingIntent = PendingIntent.getActivity (activity.getApplicationContext (), 0, intent, 0); IntentFilter [] filters = nieuw IntentFilter [1]; String [] [] techList = new String [] [] ; // Merk op dat dit hetzelfde filter is als in ons manifest. filters [0] = nieuw IntentFilter (); Filters [0] .addAction (NfcAdapter.ACTION_NDEF_DISCOVERED); Filters [0] .addCategory (Intent.CATEGORY_DEFAULT); probeer filters [0] .addDataType (MIME_TEXT_PLAIN);  catch (MalformedMimeTypeException e) gooi nieuwe RuntimeException ("Check your mime type.");  adapter.enableForegroundDispatch (activity, pendingIntent, filters, techList);  / ** * @param-activiteit De bijbehorende @link BaseActivity met het verzoek om de voorgrondverzending te stoppen. * @param-adapter De @link NfcAdapter die wordt gebruikt voor de verzending op de voorgrond. * / public static void stopForegroundDispatch (laatste activiteitsactiviteit, NfcAdapter-adapter) adapter.disableForegroundDispatch (activity); 

Wanneer u nu een tag toevoegt en onze app al is geopend, wordt onNewIntent aangeroepen en wordt er geen nieuwe activiteit gemaakt.


Gegevens lezen van een NDEF-tag

De laatste stap is om de gegevens van de tag te lezen. De uitleg wordt nogmaals op de juiste plaatsen in de code ingevoegd. Het NdefReaderTask is een privé-innerlijke klasse.

 pakket net.vrallev.android.nfc.demo; importeer java.io.UnsupportedEncodingException; import java.util.Arrays; import android.app.Activity; importeer android.app.PendingIntent; importeer android.content.Intent; importeer android.content.IntentFilter; importeer android.content.IntentFilter.MalformedMimeTypeException; import android.nfc.NdefMessage; importeer android.nfc.NdefRecord; importeer android.nfc.NfcAdapter; importeer android.nfc.Tag; importeer android.nfc.tech.Ndef; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.widget.TextView; import android.widget.Toast; / * * ... andere codedelen * / private void handleIntent (Intent opzet) String action = intent.getAction (); if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals (action)) String type = intent.getType (); if (MIME_TEXT_PLAIN.equals (type)) Tag tag = intent.getParcelableExtra (NfcAdapter.EXTRA_TAG); nieuwe NdefReaderTask (). execute (tag);  else Log.d (TAG, "Wrong mime type:" + type);  else if (NfcAdapter.ACTION_TECH_DISCOVERED.equals (action)) // In het geval we nog steeds de tag Tech Discovered Intent Tag = intent.getParcelableExtra (NfcAdapter.EXTRA_TAG) zouden gebruiken; String [] techList = tag.getTechList (); String searchedTech = Ndef.class.getName (); for (String tech: techList) if (searchedTech.equals (tech)) new NdefReaderTask (). execute (tag); breken; 
 / ** * Achtergrondtaak voor het lezen van de gegevens. Blokkeer de UI-thread niet tijdens het lezen. * * @author Ralf Wondratschek * * / privéklasse NdefReaderTask breidt AsyncTask uit @Override protected String doInBackground (Tag ... params) tag tag = params [0]; Ndef ndef = Ndef.get (tag); if (ndef == null) // NDEF wordt niet ondersteund door deze tag. return null;  NdefMessage ndefMessage = ndef.getCachedNdefMessage (); NdefRecord [] records = ndefMessage.getRecords (); for (NdefRecord ndefRecord: records) if (ndefRecord.getTnf () == NdefRecord.TNF_WELL_KNOWN && Arrays.equals (ndefRecord.getType (), NdefRecord.RTD_TEXT)) try return readText (ndefRecord);  catch (UnsupportedEncodingException e) Log.e (TAG, "Unsupported Encoding", e);  return null;  private String readText (NdefRecord record) gooit UnsupportedEncodingException / * * Zie NFC-forumspecificatie voor "Tekstrecordtype definitie" op 3.2.1 * * http://www.nfc-forum.org/specs/ * * bit_7 definieert codering * bit_6 gereserveerd voor toekomstig gebruik, moet 0 * bit_5 ... 0 lengte van IANA-taalcode * / byte [] payload = record.getPayload () zijn; // Get the Text Encoding String textEncoding = ((payload [0] & 128) == 0)? "UTF-8": "UTF-16"; // Verkrijg de taalcode in languageCodeLength = payload [0] & 0063; // String languageCode = new String (payload, 1, languageCodeLength, "US-ASCII"); // b.v. "nl" // Haal de nieuwe tekstteruglooptekst op (payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);  @Override beschermde leegte onPostExecute (String resultaat) if (result! = Null) mTextView.setText ("Leest de inhoud:" + resultaat); 

De app leest nu met succes de inhoud.



Handige apps

Om te controleren of gegevens goed worden gelezen en geschreven, gebruik ik persoonlijk graag de volgende apps:

  • NFC TagInfo door NFC Research Lab voor het lezen van gegevens
  • TagInfo door NXP SEMICONDUCTORS voor het lezen van gegevens
  • TagWriter door NXP SEMICONDUCTORS voor het schrijven van gegevens

Conclusie

In deze zelfstudie heb ik je laten zien hoe de gegevens van een NDEF-tag kunnen worden geëxtraheerd. Je zou het voorbeeld kunnen uitbreiden naar andere mime-typen en chiptechnologieën; een functie om gegevens te schrijven zou ook nuttig zijn. De eerste stap om met NFC te werken is gemaakt. De Android SDK biedt echter veel meer mogelijkheden, zoals een eenvoudige uitwisseling van gegevens (Android Beam genaamd).

Als je je Android-ontwikkeling verder wilt ontwikkelen, bekijk dan het enorme aanbod aan nuttige Android-app-sjablonen op Envato Market. Of huur een Android-ontwikkelaar bij Envato Studio.


Over de auteur

Ralf Wondratschek is een informatica-student uit Duitsland. Naast zijn studie werkt Ralf als freelancer op het gebied van mobiel computergebruik. In de afgelopen jaren heeft hij gewerkt met Java, XML, HTML, JSP, JSF, Eclipse, Google App Engine en natuurlijk Android. Hij heeft tot nu toe twee Android-apps gepubliceerd die hier te vinden zijn.

U kunt meer informatie vinden over het werk van de auteur op zijn homepage vrallev.net.


bronnen

http://www.nfc-forum.org/home/n-mark.jpg

http://commons.wikimedia.org/wiki/File%3A%C3%9Cberlagert.jpg

http://developer.android.com/images/nfc_tag_dispatch.png