国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

android nfc常用標簽讀取總結

2019-12-12 04:20:34
字體:
來源:轉載
供稿:網友

有幾天沒有更新博客了,不過本篇卻準備了許久,希望能帶給每一位開發者最簡單高效的學習方式。廢話到此為止,下面開始正文。

NFC(Near Field Communication,近場通信)是一種數據傳輸技術。與Wi-Fi、藍牙、紅外線等數據傳輸技術的一個主要差異就是有效距離一般不能超過4厘米。但是NFC傳輸速度要比紅外快。目前NFC已經出現了一些應用,例如電子標簽識別、刷手機、點對點付款、身份識別、信息記錄等,本篇文章的目的是為大家揭開NFC標簽的面紗。

NFC標簽 NFC仿真卡

下面我們先從NFC的工作模式開始闡述NFC,開發NFC必先了解NFC。

1.NFC的工作模式

NFC支持如下3種工作模式:讀卡器模式(Reader/writer mode)、仿真卡模式(Card Emulation Mode)、點對點模式(P2P mode)。

下來分別看一下這三種模式:

(1)讀卡器模式

數據在NFC芯片中,可以簡單理解成“刷標簽”。本質上就是通過支持NFC的手機或其它電子設備從帶有NFC芯片的標簽、貼紙、名片等媒介中讀寫信息。通常NFC標簽是不需要外部供電的。當支持NFC的外設向NFC讀寫數據時,它會發送某種磁場,而這個磁場會自動的向NFC標簽供電。

(2)仿真卡模式

數據在支持NFC的手機或其它電子設備中,可以簡單理解成“刷手機”。本質上就是將支持NFC的手機或其它電子設備當成借記卡、公交卡、門禁卡等IC卡使用。基本原理是將相應IC卡中的信息憑證封裝成數據包存儲在支持NFC的外設中 。
在使用時還需要一個NFC射頻器(相當于刷卡器)。將手機靠近NFC射頻器,手機就會接收到NFC射頻器發過來的信號,在通過一系列復雜的驗證后,將IC卡的相應信息傳入NFC射頻器,最后這些IC卡數據會傳入NFC射頻器連接的電腦,并進行相應的處理(如電子轉帳、開門等操作)。

(3)點對點模式

該模式與藍牙、紅外差不多,用于不同NFC設備之間進行數據交換,不過這個模式已經沒有有“刷”的感覺了。其有效距離一般不能超過4厘米,但傳輸建立速度要比紅外和藍牙技術快很多,傳輸速度比紅外塊得多,如過雙方都使用Android4.2,NFC會直接利用藍牙傳輸。這種技術被稱為Android Beam。所以使用Android Beam傳輸數據的兩部設備不再限于4厘米之內。
點對點模式的典型應用是兩部支持NFC的手機或平板電腦實現數據的點對點傳輸,例如,交換圖片或同步設備聯系人。因此,通過NFC,多個設備如數字相機,計算機,手機之間,都可以快速連接,并交換資料或者服務。

下面看一下NFC、藍牙和紅外之間的差異:

對比項 NFC 藍牙 紅外
網絡類型 點對點 單點對多點 點對點
有效距離 <=0.1m <=10m,最新的藍牙4.0有效距離可達100m 一般在1m以內,熱技術連接,不穩定
傳輸速度 最大424kbps 最大24Mbps 慢速115.2kbps,快速4Mbps
建立時間 <0.1s 6s 0.5s
安全性 安全,硬件實現 安全,軟件實現 不安全,使用IRFM時除外
通信模式 主動-主動/被動 主動-主動 主動-主動
成本

2.Android對NFC的支持

不同的NFC標簽之間差異很大,有的只支持簡單的讀寫操作,有時還會采用支持一次性寫入的芯片,將NFC標簽設計成只讀的。當然,也存在一些復雜的NFC標簽,例如,有一些NFC標簽可以通過硬件加密的方式限制對某一區域的訪問。還有一些標簽自帶操作環境,允許NFC設備與這些標簽進行更復雜的交互。這些標簽中的數據也會采用不同的格式。但Android SDK API主要支持NFC論壇標準(Forum Standard),這種標準被稱為NDEF(NFC Data Exchange Format,NFC數據交換格式)。

NDEF格式其實就類似于硬盤的NTFS,下面我們看一下NDEF數據:

(1)NDEF數據的操作

Android SDK API支持如下3種NDEF數據的操作:

1)從NFC標簽讀取NDEF格式的數據。

2)向NFC標簽寫入NDEF格式的數據。

3)通過Android Beam技術將NDEF數據發送到另一部NFC設備。

用于描述NDEF格式數據的兩個類:

1)NdefMessage:描述NDEF格式的信息,實際上我們寫入NFC標簽的就是NdefMessage對象。

2)NdefRecord:描述NDEF信息的一個信息段,一個NdefMessage可能包含一個或者多個NdefRecord。

NdefMessage和NdefRecord是Android NFC技術的核心類,無論讀寫NDEF格式的NFC標簽,還是通過Android Beam技術傳遞Ndef格式的數據,都需要這兩個類。

(2)非NDEF數據的操作

對于某些特殊需求,可能要存任意的數據,對于這些數據,我們就需要自定義格式。這些數據格式實際上就是普通的字節流,至于字節流中的數據代表什么,就由開發人員自己定義了。

(3)編寫NFC程序的基本步驟

1)設置權限,限制Android版本、安裝的設備:

<uses-sdk android:minSdkVersion="14"/><uses-permission android:name="android.permission.NFC" /><!-- 要求當前設備必須要有NFC芯片 --><uses-feature android:name="android.hardware.nfc" android:required="true" />

2)定義可接收Tag的Activity

Activity清單需要配置一下launchMode屬性:

<activity  android:name=".TagTextActivity"  android:launchMode="singleTop"/>

而Activity中,我們也抽取了一個通用的BaseNfcActivity,如下(后面的Activity實現都繼承于BaseNfcActivity):

/** * 1.子類需要在onCreate方法中做Activity初始化。 * 2.子類需要在onNewIntent方法中進行NFC標簽相關操作。 *  當launchMode設置為singleTop時,第一次運行調用onCreate方法, *  第二次運行將不會創建新的Activity實例,將調用onNewIntent方法 *  所以我們獲取intent傳遞過來的Tag數據操作放在onNewIntent方法中執行 *  如果在棧中已經有該Activity的實例,就重用該實例(會調用實例的onNewIntent()) *  只要NFC標簽靠近就執行 */public class BaseNfcActivity extends AppCompatActivity {  private NfcAdapter mNfcAdapter;  private PendingIntent mPendingIntent;  /**   * 啟動Activity,界面可見時   */  @Override  protected void onStart() {    super.onStart();    mNfcAdapter = NfcAdapter.getDefaultAdapter(this);    //一旦截獲NFC消息,就會通過PendingIntent調用窗口    mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()), 0);  }  /**   * 獲得焦點,按鈕可以點擊   */  @Override  public void onResume() {    super.onResume();    //設置處理優于所有其他NFC的處理    if (mNfcAdapter != null)      mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);  }  /**   * 暫停Activity,界面獲取焦點,按鈕可以點擊   */  @Override  public void onPause() {    super.onPause();    //恢復默認狀態    if (mNfcAdapter != null)      mNfcAdapter.disableForegroundDispatch(this);  }}

注意:通常來說,所有處理NFC的Activity都要設置launchMode屬性為singleTop或者singleTask,保證了無論NFC標簽靠近手機多少次,Activity實例只有一個。

接下來看幾個具體的NFC標簽應用實例,通過情景學習快速掌握NFC技術:

3.兩個NFC標簽的簡單實例

1.利用NFC標簽讓Android自動運行程序

場景是這樣的:現將應用程序的包寫到NFC程序上,然后我們將NFC標簽靠近Android手機,手機就會自動運行包所對應的程序,這個是NFC比較基本的一個應用。下面以貼近標簽自動運行Android自帶的“短信”為例。

向NFC標簽寫入數據一般分為三步:

1)獲取Tag對象

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

2)判斷NFC標簽的數據類型(通過Ndef.get方法)

Ndef ndef = Ndef.get(tag);

3)寫入數據

ndef.writeNdefMessage(ndefMessage);

詳細實現代碼如下:

public class RunAppActivity extends BaseNfcActivity{  private String mPackageName = "com.android.mms";//短信  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);  }  @Override  public void onNewIntent(Intent intent) {    if (mPackageName == null)      return;    //1.獲取Tag對象    Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);    writeNFCTag(detectedTag);  }  /**   * 往標簽寫數據的方法   *   * @param tag   */  public void writeNFCTag(Tag tag) {    if (tag == null) {      return;    }    NdefMessage ndefMessage = new NdefMessage(new NdefRecord[]{NdefRecord        .createApplicationRecord(mPackageName)});    //轉換成字節獲得大小    int size = ndefMessage.toByteArray().length;    try {      //2.判斷NFC標簽的數據類型(通過Ndef.get方法)      Ndef ndef = Ndef.get(tag);      //判斷是否為NDEF標簽      if (ndef != null) {        ndef.connect();        //判斷是否支持可寫        if (!ndef.isWritable()) {          return;        }        //判斷標簽的容量是否夠用        if (ndef.getMaxSize() < size) {          return;        }        //3.寫入數據        ndef.writeNdefMessage(ndefMessage);        Toast.makeText(this, "寫入成功", Toast.LENGTH_SHORT).show();      } else { //當我們買回來的NFC標簽是沒有格式化的,或者沒有分區的執行此步        //Ndef格式類        NdefFormatable format = NdefFormatable.get(tag);        //判斷是否獲得了NdefFormatable對象,有一些標簽是只讀的或者不允許格式化的        if (format != null) {          //連接          format.connect();          //格式化并將信息寫入標簽          format.format(ndefMessage);          Toast.makeText(this, "寫入成功", Toast.LENGTH_SHORT).show();        } else {          Toast.makeText(this, "寫入失敗", Toast.LENGTH_SHORT).show();        }      }    } catch (Exception e) {    }  }}

注意:設置 RunAppActivity 的 launchMode 屬性為 singleTop。

現在看一下效果圖:

將NFC標簽貼近手機背面,自動寫入數據,此時退出所有程序,返回桌面,然后再將NFC標簽貼近手機背面,將會看到自動打開了“短信”。

下來再看一個有趣的例子:

2.利用NFC標簽讓Android自動打開網頁

如何讓NFC標簽貼近手機,手機可以自動打開一個網頁呢?

首先我們創建一個NdefRecord,Android已經為我們提供好了這樣的方法:

//直接接受一個Uripublic NdefRecord createUri(String uriString); //接受一個Uri的對象public NdefRecord createUri(Uri uri); 

實現代碼對比“3.利用NFC標簽讓Android自動運行程序”部分只是修改了writeNFCTag方法中

NdefMessage ndefMessage = new NdefMessage(new NdefRecord[]{NdefRecord    .createApplicationRecord(mPackageName)});

NdefMessage ndefMessage = new NdefMessage(new NdefRecord[]{NdefRecord    .createUri(Uri.parse(http://www.baidu.com))});

其余不變。

上面這個功能還是比較有用的,例如我們往某些商品上貼上NFC標簽,里面寫入該商品的詳細介紹網頁Uri,當用戶貼近商品時,就會自動打開該商品的詳情介紹。

通過上面這兩個案例的學習相信很多人已經對NFC感起了興趣,那么下來滲透式的分析一下NDEF文本格式,看看NDEF到底是個什么東西。

4.NDEF文本格式深度解析

獲取NFC標簽中的數據要通過 NdefRecord.getPayload 方法完成。當然,在處理這些數據之前,最好判斷一下NdefRecord對象中存儲的是不是NDEF文本格式數據。

(1)判斷數據是否為NDEF格式

1)TNF(類型名格式,Type Name Format)必須是NdefRecord.TNF_WELL_KNOWN。

2)可變的長度類型必須是NdefRecord.RTD_TEXT。

如果這兩個標準同時滿足,那么就為NDEF格式。

(2)NDEF文本格式規范

不管什么格式的數據本質上都是由一些字節組成的。對于NDEF文本格式來說,這些數據的第1個字節描述了數據的狀態,然后若干個字節描述文本的語言編碼,最后剩余字節表示文本數據。這些數據格式由NFC Forum的相關規范定義,可以通過 http://members.nfc-forum.org/specs/spec_dashboard 下載相關的規范。

下面這兩張表是規范中 3.2節 相對重要的翻譯部分:

NDEF文本數據格式:

偏移量 長度(bytes) 描述
0 1 狀態字節,見下表(狀態字節編碼格式)
1 n ISO/IANA語言編碼。例如”en-US”,”fr-CA”。編碼格式是US-ASCII,長度(n)由狀態字節的后6位指定。
n+1 m 文本數據。編碼格式是UTF-8或UTF-16。編碼格式由狀態字節的前3位指定。

狀態字節編碼格式:

字節位(0是最低位,7是最高位) 含義
7 0:文本編碼為UTF-8,1:文本編碼為UTF-16
6 必須設為0
5..0 語言編碼的長度(占用的字節個數)

下面我們動手實現NFC標簽中的文本數據的讀寫操作:

1.讀NFC標簽文本數據

public class ReadTextActivity extends BaseNfcActivity {  private TextView mNfcText;  private String mTagText;  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_read_text);    mNfcText = (TextView) findViewById(R.id.tv_nfctext);  }  @Override  public void onNewIntent(Intent intent) {    //1.獲取Tag對象    Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);    //2.獲取Ndef的實例    Ndef ndef = Ndef.get(detectedTag);    mTagText = ndef.getType() + "/nmaxsize:" + ndef.getMaxSize() + "bytes/n/n";    readNfcTag(intent);    mNfcText.setText(mTagText);  }  /**   * 讀取NFC標簽文本數據   */  private void readNfcTag(Intent intent) {    if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {      Parcelable[] rawMsgs = intent.getParcelableArrayExtra(          NfcAdapter.EXTRA_NDEF_MESSAGES);      NdefMessage msgs[] = null;      int contentSize = 0;      if (rawMsgs != null) {        msgs = new NdefMessage[rawMsgs.length];        for (int i = 0; i < rawMsgs.length; i++) {          msgs[i] = (NdefMessage) rawMsgs[i];          contentSize += msgs[i].toByteArray().length;        }      }      try {        if (msgs != null) {          NdefRecord record = msgs[0].getRecords()[0];          String textRecord = parseTextRecord(record);          mTagText += textRecord + "/n/ntext/n" + contentSize + " bytes";        }      } catch (Exception e) {      }    }  }  /**   * 解析NDEF文本數據,從第三個字節開始,后面的文本數據   * @param ndefRecord   * @return   */  public static String parseTextRecord(NdefRecord ndefRecord) {    /**     * 判斷數據是否為NDEF格式     */    //判斷TNF    if (ndefRecord.getTnf() != NdefRecord.TNF_WELL_KNOWN) {      return null;    }    //判斷可變的長度的類型    if (!Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {      return null;    }    try {      //獲得字節數組,然后進行分析      byte[] payload = ndefRecord.getPayload();      //下面開始NDEF文本數據第一個字節,狀態字節      //判斷文本是基于UTF-8還是UTF-16的,取第一個字節"位與"上16進制的80,16進制的80也就是最高位是1,      //其他位都是0,所以進行"位與"運算后就會保留最高位      String textEncoding = ((payload[0] & 0x80) == 0) ? "UTF-8" : "UTF-16";      //3f最高兩位是0,第六位是1,所以進行"位與"運算后獲得第六位      int languageCodeLength = payload[0] & 0x3f;      //下面開始NDEF文本數據第二個字節,語言編碼      //獲得語言編碼      String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");      //下面開始NDEF文本數據后面的字節,解析出文本      String textRecord = new String(payload, languageCodeLength + 1,          payload.length - languageCodeLength - 1, textEncoding);      return textRecord;    } catch (Exception e) {      throw new IllegalArgumentException();    }  }}

注意:Activity清單需要配置一下launchMode屬性(后面一樣要注意):

<activity  android:name=".ReadTextActivity"  android:launchMode="singleTop"/>

2.寫NFC標簽文本數據

public class WriteTextActivity extends BaseNfcActivity {  private String mText = "NFC-NewText-123";  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_write_text);  }  @Override  public void onNewIntent(Intent intent) {    if (mText == null)      return;    //獲取Tag對象    Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);    NdefMessage ndefMessage = new NdefMessage(        new NdefRecord[] { createTextRecord(mText) });    boolean result = writeTag(ndefMessage, detectedTag);    if (result){      Toast.makeText(this, "寫入成功", Toast.LENGTH_SHORT).show();    } else {      Toast.makeText(this, "寫入失敗", Toast.LENGTH_SHORT).show();    }  }  /**   * 創建NDEF文本數據   * @param text   * @return   */  public static NdefRecord createTextRecord(String text) {    byte[] langBytes = Locale.CHINA.getLanguage().getBytes(Charset.forName("US-ASCII"));    Charset utfEncoding = Charset.forName("UTF-8");    //將文本轉換為UTF-8格式    byte[] textBytes = text.getBytes(utfEncoding);    //設置狀態字節編碼最高位數為0    int utfBit = 0;    //定義狀態字節    char status = (char) (utfBit + langBytes.length);    byte[] data = new byte[1 + langBytes.length + textBytes.length];    //設置第一個狀態字節,先將狀態碼轉換成字節    data[0] = (byte) status;    //設置語言編碼,使用數組拷貝方法,從0開始拷貝到data中,拷貝到data的1到langBytes.length的位置    System.arraycopy(langBytes, 0, data, 1, langBytes.length);    //設置文本字節,使用數組拷貝方法,從0開始拷貝到data中,拷貝到data的1 + langBytes.length    //到textBytes.length的位置    System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length);    //通過字節傳入NdefRecord對象    //NdefRecord.RTD_TEXT:傳入類型 讀寫    NdefRecord ndefRecord = new NdefRecord(NdefRecord.TNF_WELL_KNOWN,        NdefRecord.RTD_TEXT, new byte[0], data);    return ndefRecord;  }  /**   * 寫數據   * @param ndefMessage 創建好的NDEF文本數據   * @param tag 標簽   * @return   */  public static boolean writeTag(NdefMessage ndefMessage, Tag tag) {    try {      Ndef ndef = Ndef.get(tag);      ndef.connect();      ndef.writeNdefMessage(ndefMessage);      return true;    } catch (Exception e) {    }    return false;  }}

我們將手機貼近NFC標簽,當寫入成功會彈出“寫入成功”的吐司。下面我們再驗證一下是否成功寫入:

我們看到,數據已經寫入成功了,說明到此我們已經成功的讀寫NFC標簽中的文本數據了。

5.NDEF Uri格式深度解析

與NDEF文本格式一樣,存儲在NFC標簽中的Uri也有一定的格式,http://members.nfc-forum.org/specs/spec_dashboard

(1)Uri的格式規范要比文本格式簡單一些:

Name 偏移 大小 描述
識別碼 0 1byte Uri識別碼 用于存儲已知Uri的前綴
Uri字段 1 N UTF-8類型字符串 用于存儲剩余字符串

(2)Uri的前綴如下(都是十六進制的一個數):

十進制 十六進制 協議 十進制 十六進制 協議
0 0x00 N/A 1 0x01 http://www.
2 0x02 https://www. 3 0x03 http://
4 0x04 https:// 5 0x05 tel:
6 0x06 mailto: 7 0x07 ftp://anonymous:anonymous@
8 0x08 ftp://ftp. 9 0x09 ftps://
10 0x0A sftp:// 11 0x0B smb://
12 0x0C nfs:// 13 0x0D ftp://
14 0x0E dav:// 15 0x0F news:
16 0x10 telnet:// 17 0x11 imap:
18 0x12 rtsp:// 19 0x13 urn:
20 0x14 pop: 21 0x15 sip:
22 0x16 sips: 23 0x17 tftp:
24 0x18 btspp:// 25 0x19 btl2cap://
26 0x1A btgoep:// 27 0x1B tcpobex://
28 0x1C irdaobex:// 29 0x1D file://
30 0x1E urn:epc:id: 31 0x1F urn:epc:tag:
32 0x20 urn:epc:pat: 33 0x21 urn:epc:raw:
34 0x22 urn:epc: 35 0x23 urn:nfc:

每一個協議,都是用十六進制來存儲于識別碼位置(占1byte)。

是不是相對簡單了些,那么下來我們來解析一個Uri。

(3)預先定義已知Uri前綴

這里我們定義一個UriPrefix類,以便方便的獲取Uri前綴:

public class UriPrefix {  public static final Map<Byte, String> URI_PREFIX_MAP = new HashMap<Byte, String>();  // 預先定義已知Uri前綴  static {    URI_PREFIX_MAP.put((byte) 0x00, "");    URI_PREFIX_MAP.put((byte) 0x01, "http://www.");    URI_PREFIX_MAP.put((byte) 0x02, "https://www.");    URI_PREFIX_MAP.put((byte) 0x03, "http://");    URI_PREFIX_MAP.put((byte) 0x04, "https://");    URI_PREFIX_MAP.put((byte) 0x05, "tel:");    URI_PREFIX_MAP.put((byte) 0x06, "mailto:");    URI_PREFIX_MAP.put((byte) 0x07, "ftp://anonymous:anonymous@");    URI_PREFIX_MAP.put((byte) 0x08, "ftp://ftp.");    URI_PREFIX_MAP.put((byte) 0x09, "ftps://");    URI_PREFIX_MAP.put((byte) 0x0A, "sftp://");    URI_PREFIX_MAP.put((byte) 0x0B, "smb://");    URI_PREFIX_MAP.put((byte) 0x0C, "nfs://");    URI_PREFIX_MAP.put((byte) 0x0D, "ftp://");    URI_PREFIX_MAP.put((byte) 0x0E, "dav://");    URI_PREFIX_MAP.put((byte) 0x0F, "news:");    URI_PREFIX_MAP.put((byte) 0x10, "telnet://");    URI_PREFIX_MAP.put((byte) 0x11, "imap:");    URI_PREFIX_MAP.put((byte) 0x12, "rtsp://");    URI_PREFIX_MAP.put((byte) 0x13, "urn:");    URI_PREFIX_MAP.put((byte) 0x14, "pop:");    URI_PREFIX_MAP.put((byte) 0x15, "sip:");    URI_PREFIX_MAP.put((byte) 0x16, "sips:");    URI_PREFIX_MAP.put((byte) 0x17, "tftp:");    URI_PREFIX_MAP.put((byte) 0x18, "btspp://");    URI_PREFIX_MAP.put((byte) 0x19, "btl2cap://");    URI_PREFIX_MAP.put((byte) 0x1A, "btgoep://");    URI_PREFIX_MAP.put((byte) 0x1B, "tcpobex://");    URI_PREFIX_MAP.put((byte) 0x1C, "irdaobex://");    URI_PREFIX_MAP.put((byte) 0x1D, "file://");    URI_PREFIX_MAP.put((byte) 0x1E, "urn:epc:id:");    URI_PREFIX_MAP.put((byte) 0x1F, "urn:epc:tag:");    URI_PREFIX_MAP.put((byte) 0x20, "urn:epc:pat:");    URI_PREFIX_MAP.put((byte) 0x21, "urn:epc:raw:");    URI_PREFIX_MAP.put((byte) 0x22, "urn:epc:");    URI_PREFIX_MAP.put((byte) 0x23, "urn:nfc:");  }}

然后我們來看一下清單文件中Activity的相關配置:

<activity  android:name=".ReadWriteUriActivity"  android:label="讀寫NFC標簽的Uri"  android:launchMode="singleTop" >  <intent-filter>    <action android:name="android.nfc.action.NDEF_DISCOVERED" />    <category android:name="android.intent.category.DEFAULT" />    <!-- 攔截NFC標簽中存儲有以下Uri前綴的 -->    <data android:scheme="http" />    <data android:scheme="https" />    <data android:scheme="ftp" />  </intent-filter>  <intent-filter>    <action android:name="android.nfc.action.NDEF_DISCOVERED" />    <category android:name="android.intent.category.DEFAULT" />    <!-- 定義可以攔截文本 -->    <data android:mimeType="text/plain" />  </intent-filter></activity>

好了,接下來就可以進行讀寫NFC標簽中的Uri數據了:

1.讀NFC標簽中的Uri數據

public class ReadUriActivity extends BaseNfcActivity {  private TextView mNfcText;  private String mTagText;  @Override  public void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_read_uri);    mNfcText = (TextView) findViewById(R.id.tv_nfctext);  }  @Override  public void onNewIntent(Intent intent) {    //獲取Tag對象    Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);    //獲取Ndef的實例    Ndef ndef = Ndef.get(detectedTag);    mTagText = ndef.getType() + "/n max size:" + ndef.getMaxSize() + " bytes/n/n";    readNfcTag(intent);    mNfcText.setText(mTagText);  }  /**   * 讀取NFC標簽Uri   */  private void readNfcTag(Intent intent) {    if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {      Parcelable[] rawMsgs = intent.getParcelableArrayExtra(          NfcAdapter.EXTRA_NDEF_MESSAGES);      NdefMessage ndefMessage = null;      int contentSize = 0;      if (rawMsgs != null) {        if (rawMsgs.length > 0) {          ndefMessage = (NdefMessage) rawMsgs[0];          contentSize = ndefMessage.toByteArray().length;        } else {          return;        }      }      try {        NdefRecord ndefRecord = ndefMessage.getRecords()[0];        Log.i("JAVA",ndefRecord.toString());        Uri uri = parse(ndefRecord);        Log.i("JAVA","uri:"+uri.toString());        mTagText += uri.toString() + "/n/nUri/n" + contentSize + " bytes";      } catch (Exception e) {      }    }  }  /**   * 解析NdefRecord中Uri數據   * @param record   * @return   */  public static Uri parse(NdefRecord record) {    short tnf = record.getTnf();    if (tnf == NdefRecord.TNF_WELL_KNOWN) {      return parseWellKnown(record);    } else if (tnf == NdefRecord.TNF_ABSOLUTE_URI) {      return parseAbsolute(record);    }    throw new IllegalArgumentException("Unknown TNF " + tnf);  }  /**   * 處理絕對的Uri   * 沒有Uri識別碼,也就是沒有Uri前綴,存儲的全部是字符串   * @param ndefRecord 描述NDEF信息的一個信息段,一個NdefMessage可能包含一個或者多個NdefRecord   * @return   */  private static Uri parseAbsolute(NdefRecord ndefRecord) {    //獲取所有的字節數據    byte[] payload = ndefRecord.getPayload();    Uri uri = Uri.parse(new String(payload, Charset.forName("UTF-8")));    return uri;  }  /**   * 處理已知類型的Uri   * @param ndefRecord   * @return   */  private static Uri parseWellKnown(NdefRecord ndefRecord) {    //判斷數據是否是Uri類型的    if (!Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_URI))      return null;    //獲取所有的字節數據    byte[] payload = ndefRecord.getPayload();    String prefix = UriPrefix.URI_PREFIX_MAP.get(payload[0]);    byte[] prefixBytes = prefix.getBytes(Charset.forName("UTF-8"));    byte[] fullUri = new byte[prefixBytes.length + payload.length - 1];    System.arraycopy(prefixBytes, 0, fullUri, 0, prefixBytes.length);    System.arraycopy(payload, 1, fullUri, prefixBytes.length, payload.length - 1);    Uri uri = Uri.parse(new String(fullUri, Charset.forName("UTF-8")));    return uri;  }}

2.寫NFC標簽中的Uri數據

public class WriteUriActivity extends BaseNfcActivity {  private String mUri = "http://www.baidu.com";  @Override  public void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_write_uri);  }  public void onNewIntent(Intent intent) {    Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);    NdefMessage ndefMessage = new NdefMessage(new NdefRecord[]{createUriRecord(mUri)});    boolean result = writeTag(ndefMessage, detectedTag);    if (result){      Toast.makeText(this, "寫入成功", Toast.LENGTH_SHORT).show();    } else {      Toast.makeText(this, "寫入失敗", Toast.LENGTH_SHORT).show();    }  }  /**   * 將Uri轉成NdefRecord   * @param uriStr   * @return   */  public static NdefRecord createUriRecord(String uriStr) {    byte prefix = 0;    for (Byte b : UriPrefix.URI_PREFIX_MAP.keySet()) {      String prefixStr = UriPrefix.URI_PREFIX_MAP.get(b).toLowerCase();      if ("".equals(prefixStr))        continue;      if (uriStr.toLowerCase().startsWith(prefixStr)) {        prefix = b;        uriStr = uriStr.substring(prefixStr.length());        break;      }    }    byte[] data = new byte[1 + uriStr.length()];    data[0] = prefix;    System.arraycopy(uriStr.getBytes(), 0, data, 1, uriStr.length());    NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, new byte[0], data);    return record;  }  /**   * 寫入標簽   * @param message   * @param tag   * @return   */  public static boolean writeTag(NdefMessage message, Tag tag) {    int size = message.toByteArray().length;    try {      Ndef ndef = Ndef.get(tag);      if (ndef != null) {        ndef.connect();        if (!ndef.isWritable()) {          return false;        }        if (ndef.getMaxSize() < size) {          return false;        }        ndef.writeNdefMessage(message);        return true;      }    } catch (Exception e) {    }    return false;  }}

我們將手機貼近NFC標簽,寫入成功后驗證一下是否成功寫入:

讀NFC標簽中的Uri數據

我們看到,數據已經寫入成功了,說明到此我們已經成功的讀寫NFC標簽中的Uri數據了。

到這里,NDEF格式就大致說完了,那么接下來看一下非NDEF格式的數據。

6.非NDEF格式深度解析

1.MifareUltralight數據格式

將NFC標簽的存儲區域分為16個頁,每一個頁可以存儲4個字節,一個可存儲64個字節(512位)。頁碼從0開始(0至15)。前4頁(0至3)存儲了NFC標簽相關的信息(如NFC標簽的序列號、控制位等)。從第5頁開始存儲實際的數據(4至15頁)。

使用MifareUltralight.get方法獲取MifareUltralight對象,然后調用MifareUltralight.connect方法進行連接,并使用MifareUltralight.writePage方法每次寫入1頁(4個字節)。也可以使用MifareUltralight.readPages方法每次連續讀取4頁。如果讀取的頁的序號超過15,則從頭開始讀。例如,從第15頁(序號為14)開始讀。readPages方法會讀取14、15、0、1頁的數據。

2.讀MifareUltralight格式數據

public class ReadMUActivity extends BaseNfcActivity {  @Override  public void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_read_mu);  }  @Override  public void onNewIntent(Intent intent) {    Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);    String[] techList = tag.getTechList();    boolean haveMifareUltralight = false;    for (String tech : techList) {      if (tech.indexOf("MifareUltralight") >= 0) {        haveMifareUltralight = true;        break;      }    }    if (!haveMifareUltralight) {      Toast.makeText(this, "不支持MifareUltralight數據格式", Toast.LENGTH_SHORT).show();      return;    }    String data = readTag(tag);    if (data != null)      Toast.makeText(this, data, Toast.LENGTH_SHORT).show();  }  public String readTag(Tag tag) {    MifareUltralight ultralight = MifareUltralight.get(tag);    try {      ultralight.connect();      byte[] data = ultralight.readPages(4);      return new String(data, Charset.forName("GB2312"));    } catch (Exception e) {    } finally {      try {        ultralight.close();      } catch (Exception e) {      }    }    return null;  }}

3.寫MifareUltralight格式數據

public class WriteMUActivity extends BaseNfcActivity {  @Override  public void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_write_mu);  }  @Override  public void onNewIntent(Intent intent) {    Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);    String[] techList = tag.getTechList();    boolean haveMifareUltralight = false;    for (String tech : techList) {      if (tech.indexOf("MifareUltralight") >= 0) {        haveMifareUltralight = true;        break;      }    }    if (!haveMifareUltralight) {      Toast.makeText(this, "不支持MifareUltralight數據格式", Toast.LENGTH_SHORT).show();      return;    }    writeTag(tag);  }  public void writeTag(Tag tag) {    MifareUltralight ultralight = MifareUltralight.get(tag);    try {      ultralight.connect();      //寫入八個漢字,從第五頁開始寫,中文需要轉換成GB2312格式      ultralight.writePage(4, "北京".getBytes(Charset.forName("GB2312")));      ultralight.writePage(5, "上海".getBytes(Charset.forName("GB2312")));      ultralight.writePage(6, "廣州".getBytes(Charset.forName("GB2312")));      ultralight.writePage(7, "天津".getBytes(Charset.forName("GB2312")));      Toast.makeText(this, "寫入成功", Toast.LENGTH_SHORT).show();    } catch (Exception e) {    } finally {      try {        ultralight.close();      } catch (Exception e) {      }    }  }}

我們將手機貼近NFC標簽,寫入成功后驗證一下是否成功寫入:

讀NFC標簽非NDEF格式的數據

我們看到,彈出了“北京上海廣州天津”,說明數據已經寫入成功了,說明到此我們已經成功的讀寫NFC非NDEF格式的數據了。

源碼下載:demo

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 清涧县| 集安市| 淅川县| 疏勒县| 富平县| 驻马店市| 铜山县| 固阳县| 礼泉县| 隆化县| 泽普县| 大英县| 山阴县| 福建省| 莱州市| 资中县| 通城县| 武冈市| 拉萨市| 铜梁县| 山东省| 方山县| 鸡泽县| 安龙县| 湖州市| 秀山| 南宁市| 永泰县| 东源县| 屯门区| 慈溪市| 荆州市| 蒲江县| 崇左市| 襄樊市| 太谷县| 嘉善县| 罗甸县| 阜宁县| 河南省| 郴州市|