NFC 기초

이 문서에서는 Android에서 개발자가 실행하는 기본 NFC 작업을 설명합니다. NFC 데이터를 NDEF 메시지 형식으로 주고받는 방법과 이러한 기능을 지원하는 Android 프레임워크 API에 관해 설명합니다. NDEF가 아닌 데이터를 사용하는 방법을 비롯한 고급 주제는 고급 NFC를 참고하세요.

NDEF 데이터 및 Android로 작업할 때 두 가지 주요 사용 사례가 있습니다.

  • NFC 태그에서 NDEF 데이터 읽기
  • Android BeamTM을 사용하여 한 기기에서 다른 기기로 NDEF 메시지 공유하기

NFC 태그에서 NDEF 데이터를 읽는 것은 태그 디스패치 시스템으로 처리됩니다. 이 시스템은 검색된 NFC 태그를 분석하고 데이터를 적절하게 분류하며 분류된 데이터에 관심이 있는 애플리케이션을 시작합니다. 스캔된 NFC 태그를 처리하려는 애플리케이션은 인텐트 필터를 선언하고 데이터 처리를 요청할 수 있습니다.

Android BeamTM 기능을 사용하면 기기에서 물리적으로 기기를 탭하여 NDEF 메시지를 다른 기기로 푸시할 수 있습니다. NFC를 사용하면 수동 기기 검색이나 페어링이 필요하지 않기 때문에 이 상호작용은 블루투스와 같은 다른 무선 기술보다 더 쉽게 데이터를 전송할 수 있는 방법을 제공합니다. 두 기기가 범위 내에 있으면 연결이 자동으로 시작됩니다. Android Beam은 NFC API 집합을 통해 사용할 수 있으므로 모든 애플리케이션이 기기 간에 정보를 전송할 수 있습니다. 예를 들어 연락처, 브라우저, YouTube 애플리케이션은 Android Beam을 사용하여 연락처, 웹페이지, 동영상을 다른 기기와 공유합니다.

태그 디스패치 시스템

기기의 설정 메뉴에서 NFC가 사용 중지되어 있지 않은 한 일반적으로 Android 지원 기기는 화면이 잠금 해제될 때 NFC 태그를 찾습니다. Android 지원 기기가 NFC 태그를 발견하면 사용자에게 어떤 애플리케이션을 사용할지 묻지 않고 가장 적절한 활동이 인텐트를 처리하도록 하는 것이 좋습니다. 기기는 매우 짧은 범위에서 NFC 태그를 스캔하므로 사용자가 수동으로 활동을 선택하도록 하면 사용자가 기기를 태그에서 멀리 이동하게 되어 연결이 끊길 수 있습니다. 활동 선택기가 표시되지 않도록 하려면 활동에서 관심을 갖는 NFC 태그만 처리하도록 활동을 개발해야 합니다.

이 목표를 지원하기 위해 Android는 스캔된 NFC 태그를 분석하고 파싱한 후 스캔된 데이터에 관심이 있는 애플리케이션을 찾는 특수 태그 디스패치 시스템을 제공합니다. 방법은 다음과 같습니다.

  1. NFC 태그를 파싱하고 MIME 유형 또는 태그에서 데이터 페이로드를 식별하는 URI를 파악합니다.
  2. MIME 유형 또는 URI와 페이로드를 인텐트에 캡슐화합니다. 이 처음 두 단계는 NFC 태그가 MIME 유형 및 URI에 매핑되는 방법에 설명되어 있습니다.
  3. 인텐트에 기반하여 활동을 시작합니다. 자세한 내용은 NFC 태그가 애플리케이션에 디스패치되는 방법에서 설명합니다.

NFC 태그가 MIME 유형 및 URI에 매핑되는 방법

NFC 애플리케이션을 작성하기 전에 다양한 유형의 NFC 태그, 태그 디스패치 시스템이 NFC 태그를 파싱하는 방법, 태그 디스패치 시스템이 NDEF 메시지를 감지할 때 실행하는 특별한 작업을 이해하는 것이 중요합니다. NFC 태그는 다양한 기술로 제공되며 NFC 태그에는 다양한 방식으로 데이터가 기록될 수도 있습니다. Android는 NFC 포럼에서 정의하는 NDEF 표준을 가장 잘 지원합니다.

NDEF 데이터는 하나 이상의 레코드 (NdefRecord)가 포함된 메시지 (NdefMessage) 내에 캡슐화됩니다. 각 NDEF 레코드는 만들려는 레코드 유형의 사양에 따라 올바른 형식이어야 합니다. Android는 NDEF 데이터를 포함하지 않는 다른 유형의 태그도 지원합니다. NDEF 데이터는 android.nfc.tech 패키지의 클래스를 사용하여 작업할 수 있습니다. 이러한 기술에 관한 자세한 내용은 고급 NFC 주제를 참고하세요. 이러한 다른 유형의 태그를 사용하려면 태그와 통신할 수 있도록 자체 프로토콜 스택을 작성해야 합니다. 따라서 개발이 용이하고 Android 지원 기기를 최대한 지원하기 위해 가능하면 NDEF를 사용하는 것이 좋습니다.

참고: 전체 NDEF 사양을 다운로드하려면 NFC 포럼 사양 및 애플리케이션 문서 사이트로 이동하여 일반적인 유형의 NDEF 레코드 만들기에서 NDEF 레코드를 구성하는 방법의 예를 참고하세요.

NFC 태그에 관한 배경 지식이 생겼으므로 다음 섹션에서는 Android에서 NDEF 형식의 태그를 처리하는 방법을 자세히 설명합니다. Android 지원 기기는 NDEF 형식의 데이터가 포함된 NFC 태그를 검사할 때 메시지를 파싱하고 데이터의 MIME 유형 또는 식별 URI를 파악하려고 시도합니다. 이를 위해 시스템은 NdefMessage 내의 첫 번째 NdefRecord를 읽고 전체 NDEF 메시지를 해석하는 방법을 결정합니다 (한 NDEF 메시지는 여러 NDEF 레코드를 가질 수 있음). 올바른 형식의 NDEF 메시지에서는 첫 번째 NdefRecord에 다음 필드가 포함됩니다.

3비트 TNF(Type Name Format)
가변 길이 유형 필드를 해석하는 방법을 나타냅니다. 유효한 값은 표 1에 설명되어 있습니다.
가변 길이 유형
레코드 유형을 설명합니다. TNF_WELL_KNOWN를 사용하는 경우 이 필드를 사용하여 레코드 유형 정의 (RTD)를 지정합니다. 유효한 RTD 값은 표 2에 설명되어 있습니다.
가변 길이 ID
레코드의 고유 식별자입니다. 이 필드는 자주 사용되지 않지만 태그를 고유하게 식별해야 하는 경우 ID를 만들 수 있습니다.
가변 길이 페이로드
읽거나 쓰고자 하는 실제 데이터 페이로드입니다. 하나의 NDEF 메시지는 여러 NDEF 레코드를 포함할 수 있으므로 전체 페이로드가 NDEF 메시지의 첫 번째 NDEF 레코드에 있다고 가정하지 마세요.

태그 디스패치 시스템은 TNF 및 유형 필드를 사용하여 MIME 유형 또는 URI를 NDEF 메시지에 매핑하려고 합니다. 매핑에 성공하면 실제 페이로드와 함께 ACTION_NDEF_DISCOVERED 인텐트 내에 이 정보를 캡슐화합니다. 그러나 태그 디스패치 시스템이 첫 번째 NDEF 레코드를 기준으로 데이터 유형을 확인할 수 없는 경우도 있습니다. 이는 NDEF 데이터를 MIME 유형 또는 URI에 매핑할 수 없거나 NFC 태그에 시작할 NDEF 데이터가 포함되지 않은 경우에 발생합니다. 이 경우 태그 기술 및 페이로드에 관한 정보가 있는 Tag 객체는 대신 ACTION_TECH_DISCOVERED 인텐트 내에 캡슐화됩니다.

표 1에서는 태그 디스패치 시스템이 TNF 및 유형 필드를 MIME 유형 또는 URI에 매핑하는 방법을 설명합니다. 또한 어떤 TNF를 MIME 유형이나 URI에 매핑할 수 없는지도 설명합니다. 이러한 경우 태그 디스패치 시스템은 ACTION_TECH_DISCOVERED로 대체됩니다.

예를 들어 태그 디스패치 시스템은 TNF_ABSOLUTE_URI 유형의 레코드를 발견하면 이 레코드의 가변 길이 유형 필드를 URI에 매핑합니다. 태그 디스패치 시스템은 페이로드와 같은 태그에 관한 다른 정보와 함께 ACTION_NDEF_DISCOVERED 인텐트의 데이터 필드에 이 URI를 캡슐화합니다. 반면에 TNF_UNKNOWN 유형의 레코드를 발견하면 대신 태그의 기술을 캡슐화하는 인텐트를 만듭니다.

표 1. 지원되는 TNF 및 매핑

TNF(Type Name Format) 매핑
TNF_ABSOLUTE_URI 유형 필드를 기반으로 하는 URI.
TNF_EMPTY ACTION_TECH_DISCOVERED로 대체됩니다.
TNF_EXTERNAL_TYPE 유형 필드의 URN을 기반으로 하는 URI. URN은 축약된 형식(<domain_name>:<service_name>)으로 NDEF 유형 필드에 인코딩됩니다. Android는 이를 vnd.android.nfc://ext/<domain_name>:<service_name> 형식으로 URI에 매핑합니다.
TNF_MIME_MEDIA 유형 필드를 기반으로 하는 MIME 유형.
TNF_UNCHANGED 첫 번째 레코드가 잘못되어 ACTION_TECH_DISCOVERED로 대체합니다.
TNF_UNKNOWN ACTION_TECH_DISCOVERED로 대체됩니다.
TNF_WELL_KNOWN MIME 유형 또는 URI를 지정합니다. 사용 가능한 RTD 및 매핑에 관한 자세한 내용은 표 2를 참고하세요.

표 2. TNF_WELL_KNOWN 및 매핑에 지원되는 RTD

RTD(Record Type Definition) 매핑
RTD_ALTERNATIVE_CARRIER ACTION_TECH_DISCOVERED로 대체됩니다.
RTD_HANDOVER_CARRIER ACTION_TECH_DISCOVERED로 대체됩니다.
RTD_HANDOVER_REQUEST ACTION_TECH_DISCOVERED로 대체됩니다.
RTD_HANDOVER_SELECT ACTION_TECH_DISCOVERED로 대체됩니다.
RTD_SMART_POSTER 페이로드 파싱을 기반으로 하는 URI.
RTD_TEXT text/plain의 MIME 유형.
RTD_URI 페이로드를 기반으로 하는 URI.

NFC 태그가 애플리케이션에 디스패치되는 방법

태그 디스패치 시스템이 NFC 태그와 그 식별 정보를 캡슐화하는 인텐트를 만들면 인텐트를 필터링하는 관심 애플리케이션에 인텐트를 전송합니다. 둘 이상의 애플리케이션이 인텐트를 처리할 수 있는 경우 사용자가 활동을 선택할 수 있도록 활동 선택기가 표시됩니다. 태그 디스패치 시스템은 우선순위가 가장 높은 것부터 가장 낮은 것 순으로 나열된 3개의 인텐트를 정의합니다.

  1. ACTION_NDEF_DISCOVERED: 이 인텐트는 NDEF 페이로드가 포함된 태그가 스캔되고 인식되는 유형일 때 활동을 시작하는 데 사용됩니다. 이 인텐트는 우선순위가 가장 높으며, 태그 디스패치 시스템은 가능하면 항상 다른 인텐트보다 먼저 이 인텐트로 활동을 시작하려고 합니다.
  2. ACTION_TECH_DISCOVERED: ACTION_NDEF_DISCOVERED 인텐트를 처리하도록 등록된 활동이 없으면 태그 디스패치 시스템은 이 인텐트로 애플리케이션을 시작하려고 합니다. 이 인텐트는 검사되는 태그에 MIME 유형 또는 URI에 매핑할 수 없는 NDEF 데이터가 포함되어 있거나 태그에 NDEF 데이터는 없지만 알려진 태그 기술인 경우 (먼저 ACTION_NDEF_DISCOVERED를 시작하지 않음) 직접 시작됩니다.
  3. ACTION_TAG_DISCOVERED: 이 인텐트는 ACTION_NDEF_DISCOVERED 또는 ACTION_TECH_DISCOVERED 인텐트를 처리하는 활동이 없을 때 시작됩니다.

태그 디스패치 시스템의 기본 작동 방법은 다음과 같습니다.

  1. NFC 태그 (ACTION_NDEF_DISCOVERED 또는 ACTION_TECH_DISCOVERED)를 파싱할 때 태그 디스패치 시스템에서 만든 인텐트로 활동을 시작해 봅니다.
  2. 이 인텐트를 필터링하는 활동이 없으면 애플리케이션이 인텐트를 필터링하거나 태그 디스패치 시스템이 가능한 모든 인텐트를 시도할 때까지 우선순위가 다음으로 낮은 인텐트 (ACTION_TECH_DISCOVERED 또는 ACTION_TAG_DISCOVERED)로 활동을 시작해 봅니다.
  3. 인텐트를 필터링하는 애플리케이션이 없는 경우 아무것도 하지 않습니다.
그림 1. 태그 디스패치 시스템

가능한 경우 NDEF 메시지와 ACTION_NDEF_DISCOVERED 인텐트를 사용합니다. 세 가지 중 가장 구체적이기 때문입니다. 이 인텐트를 사용하면 다른 두 인텐트보다 더 적절한 시간에 애플리케이션을 시작할 수 있으므로 사용자 환경이 개선됩니다.

Android manifest에서 NFC 액세스 요청

기기의 NFC 하드웨어에 액세스하고 NFC 인텐트를 올바르게 처리하려면 먼저 AndroidManifest.xml 파일에서 다음 항목을 선언합니다.

  • NFC 하드웨어에 액세스하기 위한 NFC <uses-permission> 요소:
    <uses-permission android:name="android.permission.NFC" />
    
  • 애플리케이션이 지원할 수 있는 최소 SDK 버전. API 수준 9는 ACTION_TAG_DISCOVERED를 통해 제한된 태그 디스패치만 지원하고 EXTRA_NDEF_MESSAGES 엑스트라를 통해 NDEF 메시지에 대한 액세스만 제공합니다. 다른 태그 속성이나 I/O 작업에는 액세스할 수 없습니다. API 수준 10에는 포괄적인 판독기/작성기 지원 기능 및 포그라운드 NDEF 푸시가 포함되어 있으며, API 수준 14에서는 Android Beam을 사용하여 NDEF 메시지를 다른 기기로 더 쉽게 푸시하는 방법과 NDEF 레코드를 만드는 편리한 추가 방법을 제공합니다.
    <uses-sdk android:minSdkVersion="10"/>
    
  • NFC 하드웨어가 있는 기기의 경우에만 애플리케이션이 Google Play에 표시되도록 하는 uses-feature 요소:
    <uses-feature android:name="android.hardware.nfc" android:required="true" />
    

    애플리케이션에서 NFC 기능을 사용하지만 이 기능이 애플리케이션에 중요하지 않은 경우 uses-feature 요소를 생략하고 getDefaultAdapter()null인지 확인하여 런타임에 NFC 사용 가능 여부를 확인할 수 있습니다.

NFC 인텐트 필터링

처리하고자 하는 NFC 태그가 스캔될 때 애플리케이션을 시작하려면 애플리케이션에서 Android 매니페스트에서 NFC 인텐트를 1개, 2개 또는 3개 모두 필터링할 수 있습니다. 하지만 일반적으로 애플리케이션 시작 시기를 가장 효과적으로 제어하기 위해 ACTION_NDEF_DISCOVERED 인텐트를 필터링하는 것이 좋습니다. ACTION_NDEF_DISCOVERED을 위한 애플리케이션 필터가 없거나 페이로드가 NDEF가 아닌 경우 ACTION_TECH_DISCOVERED 인텐트는 ACTION_NDEF_DISCOVERED의 대체입니다. ACTION_TAG_DISCOVERED 필터링은 일반적으로 필터링할 수 없는 카테고리가 너무 일반적입니다. 많은 애플리케이션이 ACTION_TAG_DISCOVERED 전에 ACTION_NDEF_DISCOVERED 또는 ACTION_TECH_DISCOVERED를 필터링하므로 애플리케이션이 시작될 가능성이 낮습니다. ACTION_TAG_DISCOVEREDACTION_NDEF_DISCOVERED 또는 ACTION_TECH_DISCOVERED 인텐트를 처리할 다른 애플리케이션이 설치되어 있지 않은 경우 애플리케이션이 필터링을 위한 마지막 수단으로만 사용할 수 있습니다.

NFC 태그 배포는 다양하고 개발자가 제어할 수 없는 경우가 많기 때문에 이것이 항상 가능한 것은 아닙니다. 따라서 필요한 경우 다른 두 인텐트로 대체할 수 있습니다. 작성된 태그 및 데이터의 유형을 제어할 수 있는 경우 NDEF를 사용하여 태그 형식을 지정하는 것이 좋습니다. 다음 섹션에서는 각 인텐트 유형을 필터링하는 방법을 설명합니다.

ACTION_NDEF_발견됨

ACTION_NDEF_DISCOVERED 인텐트를 필터링하려면 필터링하려는 데이터 유형과 함께 인텐트 필터를 선언합니다. 다음 예에서는 MIME 유형이 text/plainACTION_NDEF_DISCOVERED 인텐트를 필터링합니다.

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:mimeType="text/plain" />
</intent-filter>

다음 예에서는 https://developer.android.com/index.html 형식으로 URI를 필터링합니다.

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
    <category android:name="android.intent.category.DEFAULT"/>
   <data android:scheme="https"
              android:host="developer.android.com"
              android:pathPrefix="/index.html" />
</intent-filter>

작업 기술 발견

활동이 ACTION_TECH_DISCOVERED 인텐트를 필터링하는 경우 tech-list 집합 내에서 활동이 지원하는 기술을 지정하는 XML 리소스 파일을 만들어야 합니다. tech-list 집합이 태그에서 지원하는 기술의 하위 집합인 경우 활동이 일치하는 것으로 간주됩니다. 이 집합은 getTechList()를 호출하여 가져올 수 있습니다.

예를 들어 검사된 태그가 MifareClassic, NdefFormatable, NfcA를 지원하는 경우 활동이 일치하려면 tech-list 집합에서 기술 중 세 가지, 두 개 또는 한 가지를 모두 지정해야 합니다.

다음 샘플은 모든 기술을 정의합니다. NFC 태그에서 지원하지 않는 기능은 제거해야 합니다. <project-root>/res/xml 폴더에 이 파일을 저장합니다. 원하는 이름을 자유롭게 지정할 수 있습니다.

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.IsoDep</tech>
        <tech>android.nfc.tech.NfcA</tech>
        <tech>android.nfc.tech.NfcB</tech>
        <tech>android.nfc.tech.NfcF</tech>
        <tech>android.nfc.tech.NfcV</tech>
        <tech>android.nfc.tech.Ndef</tech>
        <tech>android.nfc.tech.NdefFormatable</tech>
        <tech>android.nfc.tech.MifareClassic</tech>
        <tech>android.nfc.tech.MifareUltralight</tech>
    </tech-list>
</resources>

여러 개의 tech-list 집합을 지정할 수도 있습니다. 각 tech-list 집합은 독립적인 것으로 간주되며, 단일 tech-list 집합이 getTechList()에서 반환하는 기술의 하위 집합이라면 활동이 일치하는 것으로 간주됩니다. 이는 일치하는 기술을 위한 ANDOR 의미 체계를 제공합니다. 다음 예는 NfcA 및 Ndef 기술을 지원하거나 NfcB 및 Ndef 기술을 지원할 수 있는 태그를 매칭합니다.

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.NfcA</tech>
        <tech>android.nfc.tech.Ndef</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcB</tech>
        <tech>android.nfc.tech.Ndef</tech>
    </tech-list>
</resources>

AndroidManifest.xml 파일에서 다음 예와 같이 <activity> 요소 내 <meta-data> 요소에서 방금 만든 리소스 파일을 지정합니다.

<activity>
...
<intent-filter>
    <action android:name="android.nfc.action.TECH_DISCOVERED"/>
</intent-filter>

<meta-data android:name="android.nfc.action.TECH_DISCOVERED"
    android:resource="@xml/nfc_tech_filter" />
...
</activity>

태그 기술 및 ACTION_TECH_DISCOVERED 인텐트를 사용하는 방법에 관한 자세한 내용은 고급 NFC 문서의 지원되는 태그 기술 사용하기를 참고하세요.

ACTION_TAG_발견됨

ACTION_TAG_DISCOVERED를 필터링하려면 다음 인텐트 필터를 사용합니다.

<intent-filter>
    <action android:name="android.nfc.action.TAG_DISCOVERED"/>
</intent-filter>

인텐트에서 정보 가져오기

NFC 인텐트로 인해 활동이 시작되는 경우 인텐트에서 검사된 NFC 태그에 관한 정보를 가져올 수 있습니다. 인텐트는 검사된 태그에 따라 다음 추가 항목을 포함할 수 있습니다.

이러한 추가 항목을 가져오려면 활동이 NFC 인텐트 중 하나로 실행되었는지 확인하여 태그가 검사되었는지 확인한 다음 인텐트에서 추가 항목을 가져옵니다. 다음 예에서는 ACTION_NDEF_DISCOVERED 인텐트를 확인하고 인텐트 추가 항목에서 NDEF 메시지를 가져옵니다.

Kotlin

override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    ...
    if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
        intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.also { rawMessages ->
            val messages: List<NdefMessage> = rawMessages.map { it as NdefMessage }
            // Process the messages array.
            ...
        }
    }
}

Java

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    ...
    if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
        Parcelable[] rawMessages =
            intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
        if (rawMessages != null) {
            NdefMessage[] messages = new NdefMessage[rawMessages.length];
            for (int i = 0; i < rawMessages.length; i++) {
                messages[i] = (NdefMessage) rawMessages[i];
            }
            // Process the messages array.
            ...
        }
    }
}

또는 인텐트에서 Tag 객체를 가져올 수 있습니다. 이 객체는 페이로드를 포함하고 태그의 기술을 열거할 수 있게 해줍니다.

Kotlin

val tag: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)

Java

Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);

일반적인 NDEF 레코드 유형 만들기

이 섹션에서는 NFC 태그에 쓰거나 Android Beam으로 데이터를 전송할 때 도움이 되는 일반적인 유형의 NDEF 레코드를 만드는 방법을 설명합니다. Android 4.0 (API 수준 14)부터 createUri() 메서드를 사용하여 URI 레코드를 자동으로 만들 수 있습니다. Android 4.1 (API 수준 16)부터 MIME 및 외부 유형 NDEF 레코드를 만드는 데 도움이 되는 createExternal()createMime()를 사용할 수 있습니다. 가능한 한 이러한 도우미 메서드를 사용하여 NDEF 레코드를 수동으로 만들 때 실수를 방지하세요.

이 섹션에서는 레코드에 상응하는 인텐트 필터를 만드는 방법도 설명합니다. 이러한 모든 NDEF 레코드 예는 태그에 쓰거나 공유하는 NDEF 메시지의 첫 번째 NDEF 레코드에 있어야 합니다.

TNF_ABSOLUTE_URI

참고: TNF_ABSOLUTE_URI 대신 RTD_URI 유형을 사용하는 것이 좋습니다. 더 효율적입니다.

다음과 같은 방법으로 TNF_ABSOLUTE_URI NDEF 레코드를 만들 수 있습니다.

Kotlin

val uriRecord = ByteArray(0).let { emptyByteArray ->
    NdefRecord(
            TNF_ABSOLUTE_URI,
            "https://developer.android.com/index.html".toByteArray(Charset.forName("US-ASCII")),
            emptyByteArray,
            emptyByteArray
    )
}

Java

NdefRecord uriRecord = new NdefRecord(
    NdefRecord.TNF_ABSOLUTE_URI ,
    "https://developer.android.com/index.html".getBytes(Charset.forName("US-ASCII")),
    new byte[0], new byte[0]);

이전 NDEF 레코드의 인텐트 필터는 다음과 유사합니다.

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="https"
        android:host="developer.android.com"
        android:pathPrefix="/index.html" />
</intent-filter>

TNF_MIME_MEDIA

다음과 같은 방법으로 TNF_MIME_MEDIA NDEF 레코드를 만들 수 있습니다.

createMime() 메서드 사용:

Kotlin

val mimeRecord = NdefRecord.createMime(
        "application/vnd.com.example.android.beam",
        "Beam me up, Android".toByteArray(Charset.forName("US-ASCII"))
)

Java

NdefRecord mimeRecord = NdefRecord.createMime("application/vnd.com.example.android.beam",
    "Beam me up, Android".getBytes(Charset.forName("US-ASCII")));

수동으로 NdefRecord 만들기:

Kotlin

val mimeRecord = Charset.forName("US-ASCII").let { usAscii ->
    NdefRecord(
            NdefRecord.TNF_MIME_MEDIA,
            "application/vnd.com.example.android.beam".toByteArray(usAscii),
            ByteArray(0),
            "Beam me up, Android!".toByteArray(usAscii)
    )
}

Java

NdefRecord mimeRecord = new NdefRecord(
    NdefRecord.TNF_MIME_MEDIA ,
    "application/vnd.com.example.android.beam".getBytes(Charset.forName("US-ASCII")),
    new byte[0], "Beam me up, Android!".getBytes(Charset.forName("US-ASCII")));

이전 NDEF 레코드의 인텐트 필터는 다음과 유사합니다.

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimeType="application/vnd.com.example.android.beam" />
</intent-filter>

TNF_WELL_KNOWN with RTD_TEXT

다음과 같은 방법으로 TNF_WELL_KNOWN NDEF 레코드를 만들 수 있습니다.

Kotlin

fun createTextRecord(payload: String, locale: Locale, encodeInUtf8: Boolean): NdefRecord {
    val langBytes = locale.language.toByteArray(Charset.forName("US-ASCII"))
    val utfEncoding = if (encodeInUtf8) Charset.forName("UTF-8") else Charset.forName("UTF-16")
    val textBytes = payload.toByteArray(utfEncoding)
    val utfBit: Int = if (encodeInUtf8) 0 else 1 shl 7
    val status = (utfBit + langBytes.size).toChar()
    val data = ByteArray(1 + langBytes.size + textBytes.size)
    data[0] = status.toByte()
    System.arraycopy(langBytes, 0, data, 1, langBytes.size)
    System.arraycopy(textBytes, 0, data, 1 + langBytes.size, textBytes.size)
    return NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, ByteArray(0), data)
}

Java

public NdefRecord createTextRecord(String payload, Locale locale, boolean encodeInUtf8) {
    byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII"));
    Charset utfEncoding = encodeInUtf8 ? Charset.forName("UTF-8") : Charset.forName("UTF-16");
    byte[] textBytes = payload.getBytes(utfEncoding);
    int utfBit = encodeInUtf8 ? 0 : (1 << 7);
    char status = (char) (utfBit + langBytes.length);
    byte[] data = new byte[1 + langBytes.length + textBytes.length];
    data[0] = (byte) status;
    System.arraycopy(langBytes, 0, data, 1, langBytes.length);
    System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length);
    NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN,
    NdefRecord.RTD_TEXT, new byte[0], data);
    return record;
}

이전 NDEF 레코드의 인텐트 필터는 다음과 유사합니다.

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimeType="text/plain" />
</intent-filter>

TNF_WELL_KNOWN with RTD_URI

다음과 같은 방법으로 TNF_WELL_KNOWN NDEF 레코드를 만들 수 있습니다.

createUri(String) 메서드 사용:

Kotlin

val rtdUriRecord1 = NdefRecord.createUri("https://example.com")

Java

NdefRecord rtdUriRecord1 = NdefRecord.createUri("https://example.com");

createUri(Uri) 메서드 사용:

Kotlin

val rtdUriRecord2 = Uri.parse("https://example.com").let { uri ->
    NdefRecord.createUri(uri)
}

Java

Uri uri = Uri.parse("https://example.com");
NdefRecord rtdUriRecord2 = NdefRecord.createUri(uri);

수동으로 NdefRecord 만들기:

Kotlin

val uriField = "example.com".toByteArray(Charset.forName("US-ASCII"))
val payload = ByteArray(uriField.size + 1)                   //add 1 for the URI Prefix
payload [0] = 0x01                                           //prefixes https://www. to the URI
System.arraycopy(uriField, 0, payload, 1, uriField.size)     //appends URI to payload
val rtdUriRecord = NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, ByteArray(0), payload)

Java

byte[] uriField = "example.com".getBytes(Charset.forName("US-ASCII"));
byte[] payload = new byte[uriField.length + 1];              //add 1 for the URI Prefix
payload[0] = 0x01;                                           //prefixes https://www. to the URI
System.arraycopy(uriField, 0, payload, 1, uriField.length);  //appends URI to payload
NdefRecord rtdUriRecord = new NdefRecord(
    NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, new byte[0], payload);

이전 NDEF 레코드의 인텐트 필터는 다음과 유사합니다.

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="https"
        android:host="example.com"
        android:pathPrefix="" />
</intent-filter>

TNF_외부_유형

다음과 같은 방법으로 TNF_EXTERNAL_TYPE NDEF 레코드를 만들 수 있습니다.

createExternal() 메서드 사용:

Kotlin

var payload: ByteArray //assign to your data
val domain = "com.example" //usually your app's package name
val type = "externalType"
val extRecord = NdefRecord.createExternal(domain, type, payload)

Java

byte[] payload; //assign to your data
String domain = "com.example"; //usually your app's package name
String type = "externalType";
NdefRecord extRecord = NdefRecord.createExternal(domain, type, payload);

수동으로 NdefRecord 만들기:

Kotlin

var payload: ByteArray
...
val extRecord = NdefRecord(
        NdefRecord.TNF_EXTERNAL_TYPE,
        "com.example:externalType".toByteArray(Charset.forName("US-ASCII")),
        ByteArray(0),
        payload
)

Java

byte[] payload;
...
NdefRecord extRecord = new NdefRecord(
    NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType".getBytes(Charset.forName("US-ASCII")),
    new byte[0], payload);

이전 NDEF 레코드의 인텐트 필터는 다음과 유사합니다.

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="vnd.android.nfc"
        android:host="ext"
        android:pathPrefix="/com.example:externalType"/>
</intent-filter>

Android 지원 기기와 비 Android 지원 기기 모두를 더 효과적으로 지원하기 위해 더 일반적인 NFC 태그를 배포하려면 TNF_EXTERNAL_TYPE를 사용하세요.

참고: TNF_EXTERNAL_TYPE에 관한 URN의 표준 형식은 urn:nfc:ext:example.com:externalType이지만 NFC 포럼 RTD 사양에서는 URN의 urn:nfc:ext: 부분을 NDEF 레코드에서 생략해야 한다고 선언합니다. 따라서 도메인 (이 예에서는 example.com)과 유형 (이 예에서는 externalType)을 콜론으로 구분하기만 하면 됩니다. Android는 TNF_EXTERNAL_TYPE를 전달할 때 urn:nfc:ext:example.com:externalType URN을 vnd.android.nfc://ext/example.com:externalType URI로 변환합니다. 이 URI는 예의 인텐트 필터가 선언한 내용입니다.

Android 애플리케이션 레코드

Android 4.0 (API 수준 14)에서 도입된 Android 애플리케이션 레코드 (AAR)는 NFC 태그를 스캔할 때 애플리케이션이 시작되는 것을 더욱 확실하게 보여줍니다. AAR에는 NDEF 레코드 내에 삽입된 애플리케이션의 패키지 이름이 있습니다. Android는 전체 NDEF 메시지에서 AAR을 검색하므로 NDEF 메시지의 모든 NDEF 레코드에 AAR을 추가할 수 있습니다. AAR을 찾으면 AAR 내의 패키지 이름을 기반으로 애플리케이션을 시작합니다. 기기에 애플리케이션이 없으면 Google Play가 실행되어 애플리케이션을 다운로드합니다.

AAR은 다른 애플리케이션이 동일한 인텐트를 필터링하고 배포한 특정 태그를 처리하지 못하도록 하려는 경우에 유용합니다. AAR은 패키지 이름 제약조건으로 인해 애플리케이션 수준에서만 지원되고 인텐트 필터링과 같은 활동 수준에서는 지원되지 않습니다. 활동 수준에서 인텐트를 처리하려면 인텐트 필터를 사용합니다.

태그에 AAR이 포함된 경우 태그 디스패치 시스템은 다음과 같은 방식으로 디스패치합니다.

  1. 평상시처럼 인텐트 필터를 사용하여 활동을 시작해 봅니다. 인텐트와 일치하는 활동이 AAR과도 일치하면 활동을 시작합니다.
  2. 인텐트를 필터링하는 활동이 AAR과 일치하지 않거나 여러 활동이 인텐트를 처리할 수 있거나 인텐트를 처리할 활동이 없는 경우 AAR에 의해 지정된 애플리케이션을 시작합니다.
  3. AAR로 시작할 수 있는 애플리케이션이 없으면 Google Play로 이동하여 AAR을 기반으로 애플리케이션을 다운로드합니다.

참고: AAR과 인텐트 디스패치 시스템을 포그라운드 디스패치 시스템으로 재정의할 수 있습니다. 그러면 NFC 태그가 검색될 때 포그라운드 활동이 우선순위를 갖게 됩니다. 이 메서드를 사용하면 AAR과 인텐트 디스패치 시스템을 재정의하려면 활동이 포그라운드에 있어야 합니다.

AAR이 포함되지 않은 검사된 태그를 필터링하려는 경우 인텐트 필터를 정상적으로 선언할 수 있습니다. 이는 애플리케이션이 AAR을 포함하지 않는 다른 태그에 관심이 있는 경우에 유용합니다. 예를 들어 애플리케이션이 배포한 독점 태그는 물론 제3자가 배포한 일반 태그도 처리하도록 할 수 있습니다. AAR은 Android 4.0 이상 기기에만 해당되므로 태그를 배포할 때 AAR과 MIME 유형/URI의 조합을 사용하여 다양한 기기를 지원하는 것이 좋습니다. 또한 NFC 태그를 배포할 때 대부분의 기기 (Android 지원 및 기타 기기)를 지원할 수 있도록 NFC 태그를 작성하는 방법을 고려하세요. 애플리케이션을 더 쉽게 구별할 수 있도록 비교적 고유한 MIME 유형 또는 URI를 정의하여 지정할 수 있습니다.

Android는 AAR인 createApplicationRecord()를 생성하는 간단한 API를 제공합니다. NdefMessage의 아무 곳에나 AAR을 삽입하기만 하면 됩니다. AAR이 NdefMessage의 유일한 레코드가 아니라면 NdefMessage의 첫 번째 레코드를 사용하지 않는 것이 좋습니다. 이는 Android 시스템이 NdefMessage의 첫 번째 레코드를 확인하여 태그의 MIME 유형 또는 URI를 결정하며, 이는 애플리케이션이 필터링할 인텐트를 만드는 데 사용하기 때문입니다. 다음 코드는 AAR을 만드는 방법을 보여줍니다.

Kotlin

val msg = NdefMessage(
        arrayOf(
                ...,
                NdefRecord.createApplicationRecord("com.example.android.beam")
        )
)

Java

NdefMessage msg = new NdefMessage(
        new NdefRecord[] {
            ...,
            NdefRecord.createApplicationRecord("com.example.android.beam")}
        );
)

NDEF 메시지를 다른 기기와 공유

Android Beam을 사용하면 두 대의 Android 구동 기기 간에 간편하게 P2P 데이터를 교환할 수 있습니다. 데이터를 다른 기기로 공유하려는 애플리케이션은 포그라운드에 있어야 하며 데이터를 수신하는 기기가 잠겨 있으면 안 됩니다. 공유 기기가 수신 기기와 충분히 가까워지면 공유 기기에 '터치하여 공유하기' UI가 표시됩니다. 그러면 사용자는 수신 기기에 메시지를 공유할지 선택할 수 있습니다.

참고: API 수준 10에서는 Android Beam과 유사한 기능을 제공하는 포그라운드 NDEF 푸시를 사용할 수 있었습니다. 이러한 API는 지원 중단되었지만 이전 기기도 지원하는 데 사용할 수 있습니다. 자세한 내용은 enableForegroundNdefPush()을 참고하세요.

다음 두 가지 메서드 중 하나를 호출하여 애플리케이션에서 Android Beam을 사용하도록 설정할 수 있습니다.

  • setNdefPushMessage(): NdefMessage를 수락하여 공유할 메시지로 설정합니다. 두 기기가 충분히 가까이 있을 때 자동으로 메시지를 공유합니다.
  • setNdefPushMessageCallback(): 기기가 데이터를 공유할 범위 내에 있을 때 호출되는 createNdefMessage()가 포함된 콜백을 수락합니다. 콜백을 사용하면 필요할 때만 NDEF 메시지를 만들 수 있습니다.

활동은 한 번에 하나의 NDEF 메시지만 푸시할 수 있으므로 둘 다 설정된 경우 setNdefPushMessageCallback()setNdefPushMessage()보다 우선합니다. Android Beam을 사용하려면 다음 일반 가이드라인을 충족해야 합니다.

  • 데이터를 공유하는 활동은 포그라운드 상태여야 합니다. 두 기기 모두 화면이 잠금 해제되어 있어야 합니다.
  • 공유하는 데이터를 NdefMessage 객체에 캡슐화해야 합니다.
  • 공유된 데이터를 수신하는 NFC 기기는 com.android.npp NDEF 푸시 프로토콜 또는 NFC 포럼의 SNEP (Simple NDEF Exchange Protocol)를 지원해야 합니다. com.android.npp 프로토콜은 API 수준 9 (Android 2.3)~API 수준 13 (Android 3.2)의 기기에 필요합니다. API 수준 14 (Android 4.0) 이상에서는 com.android.npp 및 SNEP가 모두 필요합니다.

참고: 활동이 Android Beam을 사용 설정하고 포그라운드에 있는 경우 표준 인텐트 디스패치 시스템이 사용 중지됩니다. 하지만 활동이 포그라운드 디스패치도 사용 설정하는 경우에는 포그라운드 디스패치에 설정된 인텐트 필터와 일치하는 태그를 계속 스캔할 수 있습니다.

Android Beam을 사용 설정하려면 다음과 같이 하세요.

  1. 다른 기기로 푸시하려는 NdefRecord가 포함된 NdefMessage를 만듭니다.
  2. NdefMessagesetNdefPushMessage()를 호출하거나 활동의 onCreate() 메서드에서 NfcAdapter.CreateNdefMessageCallback 객체를 전달하는 setNdefPushMessageCallback를 호출합니다. 이러한 메서드에는 활성화할 기타 활동 목록(선택사항)과 함께 Android Beam으로 사용 설정할 활동이 하나 이상 필요합니다.

    일반적으로 두 기기가 통신 범위 내에 있을 때 Activity가 항상 동일한 NDEF 메시지를 푸시해야 하는 경우 setNdefPushMessage()를 사용합니다. setNdefPushMessageCallback는 애플리케이션이 애플리케이션의 현재 컨텍스트에 관심이 있고 사용자가 애플리케이션에서 실행하는 작업에 따라 NDEF 메시지를 푸시하려는 경우에 사용합니다.

다음 샘플은 간단한 활동이 활동의 onCreate() 메서드에서 NfcAdapter.CreateNdefMessageCallback를 호출하는 방법을 보여줍니다 (전체 샘플은 AndroidBeamDemo 참고). 이 예에는 MIME 레코드를 만드는 데 도움이 되는 메서드도 있습니다.

Kotlin

package com.example.android.beam

import android.app.Activity
import android.content.Intent
import android.nfc.NdefMessage
import android.nfc.NdefRecord
import android.nfc.NfcAdapter
import android.nfc.NfcAdapter.CreateNdefMessageCallback
import android.nfc.NfcEvent
import android.os.Bundle
import android.os.Parcelable
import android.widget.TextView
import android.widget.Toast
import java.nio.charset.Charset

class Beam : Activity(), NfcAdapter.CreateNdefMessageCallback {
    
    private var nfcAdapter: NfcAdapter? = null
    private lateinit var textView: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)
        textView = findViewById(R.id.textView)
        // Check for available NFC Adapter
        nfcAdapter = NfcAdapter.getDefaultAdapter(this)
        if (nfcAdapter == null) {
            Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show()
            finish()
            return
        }
        // Register callback
        nfcAdapter?.setNdefPushMessageCallback(this, this)
    }

    override fun createNdefMessage(event: NfcEvent): NdefMessage {
        val text = "Beam me up, Android!\n\n" +
                "Beam Time: " + System.currentTimeMillis()
        return NdefMessage(
                arrayOf(
                        createMime("application/vnd.com.example.android.beam", text.toByteArray())
                )
                /**
                 * The Android Application Record (AAR) is commented out. When a device
                 * receives a push with an AAR in it, the application specified in the AAR
                 * is guaranteed to run. The AAR overrides the tag dispatch system.
                 * You can add it back in to guarantee that this
                 * activity starts when receiving a beamed message. For now, this code
                 * uses the tag dispatch system.
                 *///,NdefRecord.createApplicationRecord("com.example.android.beam")
        )
    }

    override fun onResume() {
        super.onResume()
        // Check to see that the Activity started due to an Android Beam
        if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
            processIntent(intent)
        }
    }

    override fun onNewIntent(intent: Intent) {
        // onResume gets called after this to handle the intent
        setIntent(intent)
    }

    /**
     * Parses the NDEF Message from the intent and prints to the TextView
     */
    private fun processIntent(intent: Intent) {
        textView = findViewById(R.id.textView)
        // only one message sent during the beam
        intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.also { rawMsgs ->
            (rawMsgs[0] as NdefMessage).apply {
                // record 0 contains the MIME type, record 1 is the AAR, if present
                textView.text = String(records[0].payload)
            }
        }
    }
}

Java

package com.example.android.beam;

import android.app.Activity;
import android.content.Intent;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.NfcAdapter.CreateNdefMessageCallback;
import android.nfc.NfcEvent;
import android.os.Bundle;
import android.os.Parcelable;
import android.widget.TextView;
import android.widget.Toast;
import java.nio.charset.Charset;


public class Beam extends Activity implements CreateNdefMessageCallback {
    NfcAdapter nfcAdapter;
    TextView textView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        TextView textView = (TextView) findViewById(R.id.textView);
        // Check for available NFC Adapter
        nfcAdapter = NfcAdapter.getDefaultAdapter(this);
        if (nfcAdapter == null) {
            Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show();
            finish();
            return;
        }
        // Register callback
        nfcAdapter.setNdefPushMessageCallback(this, this);
    }

    @Override
    public NdefMessage createNdefMessage(NfcEvent event) {
        String text = ("Beam me up, Android!\n\n" +
                "Beam Time: " + System.currentTimeMillis());
        NdefMessage msg = new NdefMessage(
                new NdefRecord[] { createMime(
                        "application/vnd.com.example.android.beam", text.getBytes())
         /**
          * The Android Application Record (AAR) is commented out. When a device
          * receives a push with an AAR in it, the application specified in the AAR
          * is guaranteed to run. The AAR overrides the tag dispatch system.
          * You can add it back in to guarantee that this
          * activity starts when receiving a beamed message. For now, this code
          * uses the tag dispatch system.
          */
          //,NdefRecord.createApplicationRecord("com.example.android.beam")
        });
        return msg;
    }

    @Override
    public void onResume() {
        super.onResume();
        // Check to see that the Activity started due to an Android Beam
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
            processIntent(getIntent());
        }
    }

    @Override
    public void onNewIntent(Intent intent) {
        // onResume gets called after this to handle the intent
        setIntent(intent);
    }

    /**
     * Parses the NDEF Message from the intent and prints to the TextView
     */
    void processIntent(Intent intent) {
        textView = (TextView) findViewById(R.id.textView);
        Parcelable[] rawMsgs = intent.getParcelableArrayExtra(
                NfcAdapter.EXTRA_NDEF_MESSAGES);
        // only one message sent during the beam
        NdefMessage msg = (NdefMessage) rawMsgs[0];
        // record 0 contains the MIME type, record 1 is the AAR, if present
        textView.setText(new String(msg.getRecords()[0].getPayload()));
    }
}

이 코드는 삭제할 수 있는 AAR을 주석 처리합니다. AAR을 사용 설정하면 AAR에 지정된 애플리케이션이 항상 Android Beam 메시지를 수신합니다. 애플리케이션이 없으면 Google Play가 애플리케이션을 다운로드하기 위해 시작됩니다. 따라서 AAR이 사용되는 경우 Android 4.0 기기에서는 기술적으로 다음 인텐트 필터가 필요하지 않습니다.

<intent-filter>
  <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
  <category android:name="android.intent.category.DEFAULT"/>
  <data android:mimeType="application/vnd.com.example.android.beam"/>
</intent-filter>

이 인텐트 필터를 사용하면 이제 com.example.android.beam 애플리케이션이 NFC 태그를 스캔하거나 com.example.android.beam 유형의 AAR로 Android Beam을 수신할 때 또는 NDEF 형식의 메시지가 application/vnd.com.example.android.beam 유형의 MIME 레코드를 포함할 때 시작할 수 있습니다.

AAR이 애플리케이션의 시작 또는 다운로드를 보장하지만 인텐트 필터를 사용하면 AAR로 지정된 패키지 내에서 항상 기본 활동을 시작하는 대신 애플리케이션에서 원하는 활동을 시작할 수 있으므로 인텐트 필터를 사용하는 것이 좋습니다. AAR에는 활동 수준의 세분성이 없습니다. 또한 일부 Android 지원 기기는 AAR을 지원하지 않으므로 만일에 대비해 NDEF 메시지의 첫 번째 NDEF 레코드에 식별 정보를 삽입하고 필터링해야 합니다. 레코드를 만드는 방법에 관한 자세한 내용은 일반적인 유형의 NDEF 레코드 만들기를 참고하세요.