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

קריאה וכתיבה בתגים

קריאה וכתיבה בתג NFC כרוכות בהשגת התג מה-Intent לפתוח תקשורת עם התג. עליך להגדיר מקבץ פרוטוקול משלך כדי לקרוא ולכתוב נתונים לתג. עם זאת, חשוב לזכור שעדיין אפשר לקרוא ולכתוב נתוני NDEF כשעובדים ישירות באמצעות תג. אתם קובעים איך תרצו לבנות את הדברים. הדוגמה הבאה מראה איך לעבוד עם תג MIFARE Ultralight.

KotlinJava
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"))
       
}
   
}
}
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 תוכל לאכלס אותו בפרטי התג בזמן הסריקה.
      KotlinJava
      val intent = Intent(this, javaClass).apply {
          addFlags
      (Intent.FLAG_ACTIVITY_SINGLE_TOP)
      }
      var pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent,
             
      PendingIntent.FLAG_MUTABLE)
      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. שלך צריך לטפל רק באלה שדרושות לך.
      KotlinJava
      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)
      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() כדי לקבל את הסיווג של הטכנולוגיה שרוצים לתמוך.
      KotlinJava
      techListsArray = arrayOf(arrayOf<String>(NfcF::class.java.name))
      techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
  2. ביטול הקריאות החוזרות הבאות במחזור החיים של הפעילות והוספת לוגיקה להפעלה ולהשבתה של שליחה בחזית כשהפעילות מאבדת (onPause()) ומחזיר את המיקוד (onResume()). הקריאה אל enableForegroundDispatch() חייבת להיות מ: ה-thread הראשי, ורק כשהפעילות היא בחזית (קריאה ל-onResume() מבטיחה זאת). כמו כן, צריך להטמיע את הקריאה החוזרת של onNewIntent כדי לעבד את הנתונים מסריקת ה-NFC הסרוקה התיוג.
  3. KotlinJava
    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
    }
    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
    }