Android NFC近場通信01----介紹

                                                       Android NFC近場通信01----介紹

NFC(近場通信)

NFC是一套短距離的無線通信,通常距離是4釐米或更短。NFC工作頻率是13.56M Hz,傳輸速率是106kbit/s 848kbit/s. NFC總是在一個發起者和一個被動目標之間發生。發起者發出近場無線電波,這個近場可以給被動目標供電。這些被動的目標包括不需要電源的標籤,卡,也可以是有電源的設備。

與其他無線通信技術比較, 例如藍牙和WiFi NFC提供更低帶寬和距離,並且低成本,不需要供電,不需要實現匹配,整個通信過程僅僅是短短的靠近一秒就能完成。

一個帶有NFC支持的android設備通常是一個發起者。也可以作爲NFC的讀寫設備。他將檢測NFC tags並且打開一個Activity來處理. Android 2.3.3還有支持有限的P2P

Tags分很多種,其中簡單的只提供讀寫段,有的只能讀。複雜的tags可以支持一些運算,加密來控制對tags裏數據段的讀寫。甚至一些tags上有簡單的操作系統,允許一些複雜的交互和可以執行一些代碼。

API概覽

Android.nfc  package包含頂層類用來與本地NFC適配器交互這些類可以表示被檢測到的tags和用NDEF數據格式。

Class

Description

NfcManager

一個NFC adapter的管理器,可以列出所有此android設備支持的NFC adapter.只不過大部分android 設備只有一個NFC adapter,所以你大部分情況下可以直接用靜態方法 getDefaultAdapter(context)來取適配器。

NfcAdapter

表示本設備的NFC adapter,可以定義Intent來請求將系統檢測到tags的提醒發送到你的Activity.並提供方法去註冊前臺tag提醒發佈和前臺NDEF推送。 前臺NDEF推送是當前android版本唯一支持的p2p NFC通信方式。

NdefMessageandNdefRecord

NDEFNFC論壇定義的數據結構,用來有效的存數據到NFC tags.比如文本,URL,和其他MIME類型。一個NdefMessage扮演一個容器,這個容器存哪些發送和讀到的數據。一個NdefMessage對象包含或多個NdefRecord,每個NDEF record有一個類型,比如文本,URL,智慧型海報/廣告,或其他MIME數據。在NDEFMessage裏的第一個NfcRecord的類型用來發送tag到一個android設備上的activity.

Tag

標示一個被動的NFC目標,比如tagcard,鑰匙掛扣,甚至是一個電話模擬的的NFC.

當一個tag被檢測到,一個tag對象將被創建並且封裝到一個Intent裏,然後NFC 發佈系統將這個IntentstartActivity發送到註冊了接受這種Intentactivity裏。你可以用getTechList()方法來得到這個tag支持的技術細節和創建一個android.nfc.tech提供的相應的TagTechnology對象。

android.nfc.tech package 包含那些對tag查詢屬性和進行I/O操作的類。這些類分別標示一個tag支持的不同的NFC技術標準。

Class

Description

TagTechnology

這個接口是下面所有tag technology類必須實現的。

NfcA

支持ISO 14443-3A 標準的操作。Provides access to NFC-A (ISO 14443-3A) properties and I/O operations.

NfcB

Provides access to NFC-B (ISO 14443-3B) properties and I/O operations.

NfcF

Provides access to NFC-F (JIS 6319-4) properties and I/O operations.

NfcV

Provides access to NFC-V (ISO 15693) properties and I/O operations.

IsoDep

Provides access to ISO-DEP (ISO 14443-4) properties and I/O operations.

Ndef

提供對那些被格式化爲NDEFtag的數據的訪問和其他操作。

Provides access to NDEF data and operations on NFC tags that have been formatted as NDEF.

NdefFormatable

對那些可以被格式化成NDEF格式的tag提供一個格式化的操作

MifareClassic

如果android設備支持MIFARE,提供對MIFARE Classic目標的屬性和I/O操作。

MifareUltralight

如果android設備支持MIFARE,提供對MIFARE Ultralight目標的屬性和I/O操作。

聲明Android Manifest.xml的元素

在你能訪問一個設備的NFC硬件和正確的處理NFCIntent之前,需要在AndroidManifest.xml中先聲明下面的項:

1.     NFC使用 <uses-permission> 元素來訪問NFC硬件:

<uses-permission android:name="android.permission.NFC" />

2.     最小SDK版本需要設置正確,API level 9只包含有限的tag支持,包括:
    .
通過ACTION_TAG_DISCOVERED來發布Tag信息
    .
只有通過EXTRA_NDEF_MESSAGES擴展來訪問NDEF消息
    .
其他的tag屬性和I/O操作都不支持
所以你可能想要用API level 10來實現對tag的廣泛的讀寫支持。

<uses-sdk android:minSdkVersion="10"/>

3.     uses-feature 元素定義:你的程序可以再android市場裏顯示有NFC硬件。

<uses-feature android:name="android.hardware.nfc" android:required="true" />

4.      NFC intent filter告訴android系統你的activity能處理NFC數據,可以定義1個或多個intent filter:

<intent-filter>
  <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
  <data android:mimeType="mime/type" />
</intent-filter>

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

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

上邊3intent filters 有優先級,更多信息可以看下面的Tag發佈系統

也可以看NFCDemo例子的 AndroidManifest.xml來有個更深的理解。

Tag發佈系統

android設備掃描到一個NFC tag,通用的行爲是自動找最合適的Activity會處理這個tag Intent而不需要用戶來選擇哪個Activity來處理。因爲設備掃描NFC tags是在很短的範圍和時間,如果讓用戶選擇的話,那就有可能需要移動設備,這樣將會打斷這個掃描過程。你應該開發你只處理需要處理的tagsActivity,以防止讓用戶選擇使用哪個Activity來處理的情況。Android提供兩個系統來幫助你正確的識別一個NFC tag是否是你的Activity想要處理的:Intent發佈系統和前臺Activity發佈系統。

Intent發佈系統檢查所有Activitiesintent filters,找出那些定義了可以處理此tagActivity,如果有多個Activity都配置了處理同一個tag Intent,那麼將使用Activity選擇器來讓用戶選擇使用哪個Activity。用戶選擇之後,將使用選擇的Activity來處理此Intent.

前臺發佈系統允許一個Activity覆蓋掉Intent發佈系統而首先處理此tag Intent,這要求你將要處理Tag IntentActivity運行在前臺,這樣當一個NFC tag被掃描到,系統先檢測前臺的Activity是否支持處理此Intent,如果支持,即將此Intent傳給此Activity,如果不支持,則轉到Intent發佈系統。

使用Intent發佈系統

Intent發佈系統指定了3intent有不同的優先級。通常當一個tag被檢測到之後,Intent就被啓動(start)了,這個啓動遵循以下行爲:

·         android.nfc.action.NDEF_DISCOVERED這個intent是在一個包含NDEF負載的tag被檢測到時啓動,這是最高優先級的intent, android系統不會讓你指定一個Intent能處理所有的NFC數據類型,你必須在AndroidManifest.xml中指定與NFC tag對應的<data>元素,這樣當掃描到的tag傳過來的數據類型與你定義的相匹配時,你的Activity就會被調用。例如想處理一個包含plain text  NDEF_DISCOVERED intent ,你要按照如下定義AndroidManifest.xml file:

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

如果NDEF_DISCOVERED intent 已經被啓動,TECH_DISCOVERED  TAG_DISCOVERED intents 將不會被啓動。假如一個未知的tag或者不包含NDEF負載的tag被檢測到,此Intent就不會被啓動。

·         android.nfc.action.TECH_DISCOVERED如果 NDEF_DISCOVERED intent沒啓動或者沒有一個Activityfilter檢測NDEF_DISCOVERED ,並且此tag是已知的,那麼此TECH_DISCOVERED Intent將會啓動TECH_DISCOVERED intent要求你在一個資源文件裏(xml)裏指定你要支持technologies列表。更多細節請看下面的Specifying tag technologies to handle.

·         android.nfc.action.TAG_DISCOVERED如果沒有一個activity處理_DISCOVERED and TECH_DISCOVERED intents或者tag被檢測爲未知的,那麼此Intent將會被啓動。

Specifying tag technologies to handle指定處理的technologies

假如你的ActivityAndroidManifest.xml文件裏聲明瞭處理android.nfc.action.TECH_DISCOVERED intent ,你必須創建一個Xml格式的資源文件,並加上你的activity支持的technologiestech-list集合裏。這樣你的activity將被認作能處理這些tech-list的處理者,如果tag使用的technology屬於你的定義的list裏,你的Activity將接收此Intent。你可以用getTechList()來獲得tag支持的technologies

例如:如果一個tag被檢測到支持MifareClassic, NdefFormatable,  NfcA,你的tech-list集合必須指定了其中的一項或者多項來保證你的Activity能處理此Intent

下面是一個資源文件例子,定義了所有的technologies. 你可以根據需要刪掉不需要的項,將此文件以任意名字+.xml保存到<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集合是getTechList()返回的technologies集合的子集,那麼你的Activity將被認爲匹配了。這個還提供操作。下面的例子表示支持 NfcANDef的卡,或者支持NfcBNDef的卡:

<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>
</resources>

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

 AndroidManifest.xml 文件中指定這個tech-list資源文件的方法是在<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>

使用前臺發佈系統Using the foreground dispatch system

前臺發佈系統允許一個Activity 攔截一個tag Intent 獲得最高優先級的處理,這種方式很容易使用和實現:

1.     添加下列代碼到ActivityonCreate() 方法裏

a.     創建一個 PendingIntent 對象這樣Android系統就能在一個tag被檢測到時定位到這個對象

PendingIntent pendingIntent = PendingIntent.getActivity(
    this, , new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), );

b.    Intent filters裏聲明你想要處理的Intent,一個tag被檢測到時先檢查前臺發佈系統,如果前臺Activity符合Intent filter的要求,那麼前臺的Activity的將處理此Intent。如果不符合,前臺發佈系統將Intent轉到Intent發佈系統。如果指定了nullIntent filters,當任意tag被檢測到時,你將收到TAG_DISCOVERED intent。因此請注意你應該只處理你想要的Intent

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

c.     設置一個你程序要處理的Tag technologies的列表,調用Object.class.getName() 方法來獲得你想要支持處理的technology類。


  techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
 

2.     覆蓋下面的方法來打開或關閉前臺發佈系統。比如onPause()onResume()方法。必須在主線程裏調用enableForegroundDispatch(Activity, PendingIntent, IntentFilter[], String[][]) 而且Activity在前臺(可以在onResume()裏調用來保證這點)。你也要覆蓋onNewIntent回調來處理得到的NFC tag數據。

public void onPause() {
    super.onPause();
    mAdapter.disableForegroundDispatch(this);
}   

public void onResume() {
    super.onResume();
    mAdapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray);
}

public void onNewIntent(Intent intent) {
    Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    //do something with tagFromIntent
}

See the ForegroundDispatch sample from API Demos for the complete sample.

使用NFC tag上的數據

NFC tag上的數據是以字節存放,所以你可以將其轉換成其他你想要的格式。當往tag寫東西時,你必須以字節格式來寫。Android提供API來幫助寫符合NDEF標準的信息。使用此標準能保證你的數據在往tag寫時能被所有Android NFC設備支持。然而,很多tag使用他們自己的標準來存儲數據,這些標準也被Android支持。但你必須自己實現協議棧來讀寫這些tag。你可以在android.nfc.tech裏找到所有支持的technologies,並且可以在TagTechnology接口裏對technology有個瞭解。這一段是簡單介紹在android系統裏怎樣使用NDEF 消息。這不意味着是一個完整的NDEF功能的介紹。但標出了主要需要注意和使用的東西。

爲了方便使用NDEF消息,android提供NdefRecord  NdefMessage來包裝原始字節數據爲NDEF消息。一個NdefMessage是保存個或多個NdefRecords的容器。每個NdefRecord有自己的唯一類型名字格式,記錄類型和ID來與其他記錄區分開。你可以存儲不同類型的記錄,不同的長度到同一個 NdefMessageNFC tag容量的限制決定你的NdefMessage的大小。
那些支持NdefNdefFormatable技術的tag可以返回和接受NdefMessage對象爲參數來進行讀寫操作。你需要創建你自己的邏輯來爲其他在android.nfc.techtag技術實現讀寫字節的操作。

你可以從NFC Forum(http://www.nfc-forum.org/specs/)下載NDEF消息標準的技術文檔,比如純文本和智慧型海報. NFCDemo例子裏聲明瞭純文本和智慧型海報的NDef 消息。

讀一個NFC tag

當一個NFC tag靠近一個NFC設備,一個相應的Intent將在設備上被創建。然後通知合適的程序來處理此Intent
下面的方法處理TAG_DISCOVERED intent並且使用迭代器來獲得包含在NDEF tag負載的數據

NdefMessage[] getNdefMessages(Intent intent) {
    
// Parse the intent
    
NdefMessage[] msgs = null;
    
String action = intent.getAction();
    
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) {
        
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
        
if (rawMsgs != null) {
            msgs 
= new NdefMessage[rawMsgs.length];
            
for (int i = ; i < rawMsgs.length; i++) {
                msgs
[i] = (NdefMessage) rawMsgs[i];
            
}
        
}
        
else {
        
// Unknown tag type
            
byte[] empty = new byte[] {};
            
NdefRecord record = new NdefRecord(NdefRecord.TNF_UNKNOWN, empty, empty, empty);
            
NdefMessage msg = new NdefMessage(new NdefRecord[] {record});
            msgs 
= new NdefMessage[] {msg};
        
}
    
}        
    
else {
        
Log.e(TAG, "Unknown intent " + intent);
        finish
();
    
}
    
return msgs;
}

請記住NFC設備讀到的數據是byte類型,所以你可能需要將他轉成其他格式來呈現給用戶。NFCDemo例子展示了怎樣用com.example.android.nfc.record中的類來解析NDEF消息,比如純文本和智慧型海報。

NFC tag

NFC tag寫東西涉及到構造一個NDEF 消息和使用與tag匹配的Tag技術。下面的代碼展示怎樣寫一個簡單的文本到NdefFormatable tag

NdefFormatable tag = NdefFormatable.get(t);
Locale locale = Locale.US;
final byte[] langBytes = locale.getLanguage().getBytes(Charsets.US_ASCII);
String text = "Tag, you're it!";
final byte[] textBytes = text.getBytes(Charsets.UTF_8);
final int utfBit = ;
final char status = (char) (utfBit + langBytes.length);
final byte[] data = Bytes.concat(new byte[] {(byte) status}, langBytes, textBytes);
NdefRecord record = NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[], data);
try {
    
NdefRecord[] records = {text};
    
NdefMessage message = new NdefMessage(records);
    tag
.connect();
    tag
.format(message);
}
catch (Exception e){
    
//do error handling
}

點對點的數據交換

前臺推送技術支持簡單點對點的數據交換,你可以用enableForegroundNdefPush(Activity, NdefMessage) 方法來打開此功能.爲了用這個功能:

·         推送數據的Activity必須是前臺Activity

·         你必須將你要發送的數據封裝到NdefMessage對象裏。

·         接收推送數據的設備必須支持com.android.npp  NDEF推送協議,這個對於Android設備是可選的

假如你的Activity打開了前臺推送功能並且位於前臺,這時標準的Intent發佈系統是禁止的。然而,如果你的Activity允許前臺發佈系統,那麼此時檢測tag的功能仍然是可用的,不過只適用於前臺發佈系統。

要打開前臺推送:

1.     創建一個你要推送給其他NFC設備的包含NdefRecordsNdefMessage

2.     在你的Activity裏實現onResume()  onPause() 的回調來正確處理前臺推送的生命週期。你必須在你的Activity位於前臺並在主線程裏調用enableForegroundNdefPush(Activity, NdefMessage) (可以在onResume()裏調用來保證這點).

public void onResume() {
    super.onResume();
    if (mAdapter != null)
        mAdapter.enableForegroundNdefPush(this, myNdefMessage);
}
public void onPause() {
    super.onPause();
    if (mAdapter != null)
        mAdapter.disableForegroundNdefPush(this);
}

Activity位於前臺,你可以靠近另外一個NFC設備來推送數據。


PS:由於這是很久之前保下來供自己學習的,忘了地址了,從網上一查都是抄來抄去,找不到原作者的鏈接了,再此感謝原作者的付出!


發佈了82 篇原創文章 · 獲贊 76 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章