고급 NFC 개요

이 문서에서는 다양한 태그 기술을 사용한 작업, NFC 태그에 쓰기 및 포그라운드 디스패치(다른 애플리케이션이 인텐트를 필터링하더라도 포그라운드 애플리케이션이 동일한 인텐트를 처리할 수 있음)와 같은 고급 NFC 주제에 대해 설명합니다.

지원되는 태그 기술 사용

NFC 태그 및 Android 구동 기기로 작업하는 경우 태그에서 데이터를 읽고 쓰는 데 사용하는 기본 형식은 NDEF입니다. 기기가 NDEF 데이터로 태그를 검사하면 Android는 메시지를 파싱하고 가능한 경우 NdefMessage에 전달하도록 지원합니다. 그러나 NDEF 데이터가 포함되지 않은 태그를 검사하거나 NDEF 데이터를 MIME 유형 또는 URI에 매핑할 수 없는 경우도 있습니다. 이러한 경우에는 태그와 직접 통신하여 자신의 프로토콜(원시 바이트)로 읽고 쓸 수 있어야 합니다. Android는 android.nfc.tech 패키지로 이러한 사용 사례의 일반적인 지원을 제공합니다(표 1에서 설명). getTechList() 메서드를 사용하여 태그에서 지원하는 기술을 확인하고, android.nfc.tech에서 제공하는 클래스 중 하나를 사용하여 해당하는 TagTechnology 객체를 만들 수 있습니다.

표 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 이 Android 기기가 MIFARE를 지원하는 경우 MIFARE Classic 속성 및 I/O 작업에 액세스하도록 지원합니다.
MifareUltralight 이 Android 기기가 MIFARE를 지원하는 경우 MIFARE Ultralight 속성 및 I/O 작업에 액세스하도록 지원합니다.

태그 기술 및 ACTION_TECH_DISCOVERED 인텐트 사용

NDEF 데이터가 있지만 MIME 또는 URI에 매핑할 수 없는 태그를 기기에서 검사할 경우 태그 디스패치 시스템은 ACTION_TECH_DISCOVERED 인텐트로 활동을 시작하려고 시도합니다. ACTION_TECH_DISCOVERED는 비 NDEF 데이터가 있는 태그를 검사할 경우에도 사용됩니다. 이 대체를 사용하면 태그 디스패치 시스템에서 태그를 파싱할 수 없는 경우 태그의 데이터로 직접 작업할 수 있습니다. 태그 기술로 작업할 경우 기본 단계는 다음과 같습니다.

  1. 처리할 태그 기술을 지정하는 ACTION_TECH_DISCOVERED 인텐트를 필터링합니다. 자세한 내용은 NFC 인텐트 필터링을 참조하세요. NDEF 메시지를 MIME 유형 또는 URI에 매핑할 수 없거나 검사한 태그에 NDEF 데이터가 포함되지 않은 경우 일반적으로 태그 디스패치 시스템은 ACTION_TECH_DISCOVERED 인텐트를 시작하려고 시도합니다. 자세한 내용은 태그 디스패치 시스템을 참조하세요.
  2. 애플리케이션이 인텐트를 수신하면 인텐트에서 Tag 객체를 가져옵니다.

    Kotlin

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

    자바

        Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        
  3. android.nfc.tech 패키지에서 클래스의 get 팩토리 메서드 중 하나를 호출하여 TagTechnology의 인스턴스를 가져옵니다. get 팩토리 메서드를 호출하기 전에 getTechList()를 호출하여 지원되는 태그의 기술을 열거할 수 있습니다. 예를 들어 Tag에서 MifareUltralight의 인스턴스를 가져오려면 다음과 같이 하세요.

    Kotlin

        MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG))
        

    자바

        MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG));
        

태그 읽기 및 쓰기

NFC 태그를 읽고 쓰려면 인텐트에서 태그를 가져오고 태그와의 통신을 시작해야 합니다. 태그에서 데이터를 읽고 쓰려면 자체 프로토콜 스택을 정의해야 합니다. 하지만 태그로 직접 작업할 경우 여전히 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"))
                }
            }
        }
    }
    

자바

    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. 활동의 onCreate() 메서드에서 다음 코드를 추가합니다.
    1. 태그 검사 시 Android 시스템이 태그의 세부정보를 채울 수 있도록 PendingIntent 객체를 만듭니다.

      Kotlin

          val intent = Intent(this, javaClass).apply {
              addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
          }
          var pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
          

      자바

          PendingIntent pendingIntent = PendingIntent.getActivity(
              this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
          
    2. 가로챌 인텐트를 처리하기 위해 인텐트 필터를 선언합니다. 포그라운드 디스패치 시스템은 기기가 태그를 검사할 때 받는 인텐트를 사용하여 지정된 인텐트 필터를 확인합니다. 일치하는 경우 애플리케이션이 인텐트를 처리합니다. 일치하지 않는 경우 포그라운드 디스패치 시스템이 인텐트 디스패치 시스템으로 대체됩니다. 인텐트 필터 및 기술 필터의 null 배열을 지정하면 TAG_DISCOVERED 인텐트로 대체되는 모든 태그를 필터링하게 됩니다. 아래의 코드 스니펫은 NDEF_DISCOVERED의 모든 MIME 유형을 처리합니다. 필요한 항목만 처리해야 합니다.

      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)
          

      자바

          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))
          

      자바

          techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
          
  2. 다음 활동 수명 주기 콜백을 재정의하고, 활동이 포커스를 잃고(onPause()) 다시 얻을 때(onResume()) 포그라운드 디스패치를 사용 설정 및 사용 중지할 로직을 추가합니다. enableForegroundDispatch()는 기본 스레드에서 호출해야 하며, 활동이 포그라운드에 있을 경우에만(onResume()에서 호출 시 보장됨) 호출해야 합니다. 검사된 NFC 태그에서 데이터를 처리하려면 onNewIntent 콜백도 구현해야 합니다.
  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
        }
        

    자바

        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
        }
        

전체 샘플은 API 데모의 ForegroundDispatch 샘플을 참조하세요.