2013年3月21日 星期四

[Android] Android Developer Note Connectivity 3

Advanced NFC (見Android Developer Advanced NFC)

序文:
這份文件描述了比較進階部分的NFC主題,像是處理不同種類的tag technologies,
寫入NFC tags,以及前景的調度(dispatching),它允許一個app在前景可以處理intents,
即便其他app篩相同的東西。

Working with Supported Tag Technologies
當用NFC tags及Android的裝置時,主要使用的讀寫格式是NDEF。
當一個裝置掃一個含NDEF data的tag,Android提供對於message的parsing支援,
並如果可以的話遞交一個NdefMessage
有些狀況是當你掃一個不含NDEF資料的tag的時候,
你需要直接和tag開啟溝通並自己寫自己的protocol(in raw bytes)。
Android對此狀況提供了一般的支援在 android.nfc.tech package裡,
定義在下面的第一張表。你可以使用getTechList()method,
來決定被支援的technologies,且製作對應的TagTechnology物件,
使用android.nfc.tech提供的classes。

ClassDescription
TagTechnologyThe interface that all tag technology classes must implement.
NfcAProvides access to NFC-A (ISO 14443-3A) properties and I/O operations.
NfcBProvides access to NFC-B (ISO 14443-3B) properties and I/O operations.
NfcFProvides access to NFC-F (JIS 6319-4) properties and I/O operations.
NfcVProvides access to NFC-V (ISO 15693) properties and I/O operations.
IsoDepProvides access to ISO-DEP (ISO 14443-4) properties and I/O operations.
NdefProvides access to NDEF data and operations on NFC tags that have been formatted as NDEF.
NdefFormatableProvides a format operations for tags that may be NDEF formattable.

下面的tag technologies並不被要求在Android裝置上支援。
ClassDescription
MifareClassicProvides access to MIFARE Classic properties and I/O operations, if this Android device supports MIFARE.
MifareUltralightProvides access to MIFARE Ultralight properties and I/O operations, if this Android device supports MIFARE.

Working with tag technologies and the ACTION_TECH_DISCOVERED intent
當一個裝置掃描一個上面帶有NDEF資料的tag,但沒辦法對應到MIME或URI,
tag dispatch系統會嘗試去以 ACTION_TECH_DISCOVERED intent來開它。(上一篇有提過) ACTION_TECH_DISCOVERED也會被使用,當一個非NDEF資料的tag被掃描時。
因為有這個fallback,允許你直接在tag上的資料作處理。
(如果tag dispatch系統沒辦法幫你parse的話)
基本的步驟如下:
1. 去篩ACTION_TECH_DISCOVERED的intent,條件設為篩你想處理的那些tag technologies。
會做什麼事情前面那篇也都講了。
2. 當你的app收到intent時,從intent裡抓取Tag物件:

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

3. 藉由呼叫android.nfc.tech package裡的 get factory methods,
取得TagTechnology的實例(instance)。你可以枚舉(enumerate)支援的technologies of the tags,
藉由在呼叫get factory method之前先呼叫getTechList()
舉例來說你想從Tag獲得一個MifareUltralight的實例,照下面這樣作:

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


Reading and writing to tags
讀寫一個NFC tag的過程,跟從intent獲得tag以及開啟和tag溝通有關。
你必須定義你自己的protocol stack來讀寫資料到tag。
記得,不論如何,當你要直接處理tag時,你仍然是要讀寫NDEF資料。
是否要不要作架構的決定權在你。
下面的例子展示了如何去處理一個MIFARE Ultralight tag。


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 closing 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 writing MifareUltralight
            message...", e);
        } finally {
            if (mifare != null) {
               try {
                   mifare.close();
               }
               catch (IOException e) {
                   Log.e(TAG, "Error closing tag...", e);
               }
            }
        }
        return null;
    }
}


Using the Foreground Dispatch System
前景調度系統允許一個activity去截下一個intent,
並且聲稱比其他的activities有更高的優先權來處理同一個intent。
使用這個系統,要引入建構一些資料結構給Android系統能夠傳送適當的intents到你的app。
要打開這個系統:
1. 加入下面的code到onCreate() activity的method裡:
  a. 作一個PendingIntent物件讓Android系統可以將詳細的tag資訊在被掃的時候能將其填進去
  PendingIntent pendingIntent = PendingIntent.getActivity( this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
  b. 聲明intent filters來處理你想攔下的intents。
      前景調度系統在裝置掃到tag並傳出intent時會檢查指定的的intent filters。
      如果相符那麼你的app會處理intent。若否,前景調度系統會falls back到intent dispatch system。
      指定一個null的intent filters和technology filters的陣列,
      指定你要篩所有fallback到TAG_DISCOVERED intent的tags。
      下面的程式碼片段處理所有NDEF_DISCOVERED的MIME types。
      你應該只去處理你需要的部分。

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. 設定好你的app想要處理的一個tag technologies的陣列。
       呼叫Object.class.getName() method來獲取你要支援的technology的class。

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


2. 複寫下面的activity生命週期的callbacks,並加上邏輯去控制前景調度的開關。
(當activity失去(onPause()) 和重新獲得 (onResume()焦點(focus))
enableForegroundDispatch()必須被從main thread來呼叫,
且只有當activity是在前景時這麼做。(呼叫onResume()可以保證這點)。
你也需要去實作onNewIntent callback來從被掃的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


可以參考API Demo的 ForegroundDispatch sample來看完整的範例。






沒有留言:

張貼留言