סקירה כללית של Advanced NFC

במסמך הזה מתוארים נושאים מתקדמים על NFC, כמו עבודה עם טכנולוגיות תגים שונות, כתיבה לתגי NFC ושליחה בחזית, שמאפשרים לאפליקציה בחזית להתמודד עם כוונות גם כשאפליקציות אחרות מסוננות לפי אותן כוונות.

עבודה עם טכנולוגיות תגים נתמכות

כשעובדים עם תגי NFC ועם מכשירים מבוססי Android, הפורמט העיקרי שבו משתמשים כדי לקרוא וכתיבת נתונים בתגים היא NDEF. כשמכשיר סורק תג עם נתוני NDEF, Android מספק תמיכה בניתוח ההודעה ובמסירה שלה בNdefMessage ככל האפשר. עם זאת, במקרים מסוימים כאשר סורקים תג שלא מכיל נתוני NDEF או כאשר לא ניתן למפות את נתוני NDEF לסוג MIME או URI. במקרים כאלה, צריך לפתוח תקשורת ישירות עם התג ולקרוא בו ולכתוב בו בפרוטוקול משלכם (בבייטים גולמיים). מערכת Android מספקת תמיכה כללית לתרחישים לדוגמה האלה באמצעות חבילת android.nfc.tech, המתוארת בטבלה 1. אפשר להשתמש בשיטה getTechList() כדי לזהות את הטכנולוגיות שנתמכות על ידי התג, ויוצרים את הערך המתאים של TagTechnology אובייקט עם אחת המחלקות שסופקה על ידי android.nfc.tech

טבלה 1. טכנולוגיות תגים נתמכות

דרגה תיאור
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 גישה לנתונים ולפעולות של NDEF בתגי NFC בפורמט NDEF.
NdefFormatable כוללת פעולות פורמט לתגים שעשויים להיות בפורמט NDEF.

טכנולוגיות התגים הבאות לא נתמכות במכשירים מבוססי Android.

טבלה 2. טכנולוגיות אופציונליות של תגים נתמכות

דרגה תיאור
MifareClassic גישה לנכסים של MIFARE Classic ולפעולות קלט/פלט (I/O), אם מכשיר Android הזה שתומך ב-MIFARE.
MifareUltralight מספקת גישה למאפייני MIFARE Ultralight ופעולות קלט/פלט (I/O), אם מערכת ההפעלה Android שהמכשיר תומך ב-MIFARE.

עבודה עם טכנולוגיות תגים ו-Intent ACTION_TECH_DISCOVERED

כשמכשיר סורק תג שיש בו נתוני NDEF אבל לא ניתן למפות אותו ל-MIME או URI, מערכת שליחת התגים מנסה להתחיל פעילות עם ACTION_TECH_DISCOVERED בכוונה טובה. השדה ACTION_TECH_DISCOVERED משמש גם לתג עם נתונים שאינם של NDEF. החלופה הזו מאפשרת לעבוד עם הנתונים בתג ישירות אם מערכת שליחת התגים לא הצליחה לנתח אותו בשבילכם. השלבים הבסיסיים בעבודה עם אלו הן טכנולוגיות התגים:

  1. סינון לפי Intent של ACTION_TECH_DISCOVERED שמציין את שבהן רוצים לטפל. למידע נוסף על סינון לפי NFC Intents לקבלת מידע נוסף. באופן כללי, מערכת שליחת התגים מנסה להפעיל Intent מסוג ACTION_TECH_DISCOVERED כשהודעת NDEF לא ניתן למפות לסוג MIME או ל-URI, או אם התג שנסרק לא הכיל נתוני NDEF. עבור מידע נוסף על האופן שבו נקבע המאפיין הזה זמין במאמר מערכת שליחת התגים.
  2. כשהאפליקציה מקבלת את ה-Intent, צריך לקבל את האובייקט Tag מ- הכוונה:

    Kotlin

    var tagFromIntent: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
    

    Java

    Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    
  3. כדי לקבל מופע של TagTechnology, מפעילים את אחת מהאפשרויות get שיטות המקוריות של המחלקות בחבילה android.nfc.tech. אפשר לספור את הטכנולוגיות הנתמכות של התג באמצעות קריאה ל-getTechList() לפני קריאה ל-method של היצרן get. לדוגמה, כדי לקבל מכונה של MifareUltralight מ-Tag, צריך לבצע את הפעולות הבאות:

    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;
    }
}

שימוש במערכת השליחה בחזית

מערכת השליחה בחזית מאפשרת לפעילות לקלוט כוונה וטענה עדיפות על פני פעילויות אחרות שמטפלות באותה כוונה. השימוש במערכת הזו כרוך לבנות כמה מבני נתונים כדי שמערכת Android תוכל לשלוח את את הכוונות שלכם. כדי להפעיל את מערכת השליחה בחזית:

  1. מוסיפים את הקוד הבא ל-method onCreate() של הפעילות:
    1. יצירת אובייקט 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);
      
    2. צריך להצהיר על מסנני Intent כדי לטפל באובייקטים של Intent שרוצים ליירט. החזית המערכת בודקת את מסנני ה-Intent שצוינו באמצעות הכוונה שמתקבלת שהמכשיר סורק תג. אם הוא תואם, האפליקציה תטפל ב-Intent. אם כן לא תואם, מערכת שליחת המידע בחזית תוחזר למערכת הפצת ה-Intent. ציון מערך null של מסנני Intent ומסנני טכנולוגיה, מציין שרוצים לסנן כדי להציג את כל התגים שמבוססים על TAG_DISCOVERED בכוונה טובה. קטע הקוד שבהמשך מטפל בכל סוגי MIME עבור NDEF_DISCOVERED. שלך צריך לטפל רק באלה שדרושות לך.

      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, };
      
    3. מגדירים מערך של טכנולוגיות תגים שהאפליקציה שלכם רוצה לטפל בהן. קוראים לפונקציה השיטה Object.class.getName() כדי לקבל את הסיווג של הטכנולוגיה שרוצים לתמוך.

      Kotlin

      techListsArray = arrayOf(arrayOf<String>(NfcF::class.java.name))
      

      Java

      techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
      
  2. ביטול הקריאות החוזרות הבאות במחזור החיים של הפעילות והוספת לוגיקה להפעלה ולהשבתה של שליחה בחזית כשהפעילות מאבדת (onPause()) ומחזיר את המיקוד (onResume()). הקריאה אל enableForegroundDispatch() חייבת להיות מ: ה-thread הראשי, ורק כשהפעילות היא בחזית (קריאה ל-onResume() מבטיחה זאת). כמו כן, צריך להטמיע את הקריאה החוזרת של onNewIntent כדי לעבד את הנתונים מסריקת ה-NFC הסרוקה התיוג.
  3. 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
    }