日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android NFC 标签读写读取快速开发教程 ( 整理来自 https://blog.csdn.net/wolfking0608/article/details/72675180 )

發布時間:2023/12/9 Android 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android NFC 标签读写读取快速开发教程 ( 整理来自 https://blog.csdn.net/wolfking0608/article/details/72675180 ) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文整理完全參考 :?https://blog.csdn.net/wolfking0608/article/details/72675180?

只是將參考文章做了瀏覽處理

我在文章結尾放入了我的實現方式 , 分為:?Activity , xml 配置 , NfcUtils

?

Demo下載

1.NFC的工作模式

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

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

(1)讀卡器模式

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

(2)仿真卡模式

數據在支持NFC的手機或其它電子設備中,可以簡單理解成“刷手機”。本質上就是將支持NFC的手機或其它電子設備當成借記卡、公交卡、門禁卡等IC卡使用?;驹硎菍⑾鄳狪C卡中的信息憑證封裝成數據包存儲在支持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.1s6s0.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版本、安裝的設備:

在 AndroidManifest 配置權限

<uses-permission android:name="android.permission.NFC" /><uses-sdkandroid:minSdkVersion="10"android:targetSdkVersion="19" /><uses-featureandroid:name="android.hardware.nfc"android:required="true" />

2)定義可接收Tag的Activity

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

<activityandroid: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,界面可見時*/@Overrideprotected void onStart() {super.onStart();mNfcAdapter = NfcAdapter.getDefaultAdapter(this);//一旦截獲NFC消息,就會通過PendingIntent調用窗口mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()), 0);}/*** 獲得焦點,按鈕可以點擊*/@Overridepublic void onResume() {super.onResume();//設置處理優于所有其他NFC的處理if (mNfcAdapter != null)mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);}/*** 暫停Activity,界面獲取焦點,按鈕可以點擊*/@Overridepublic 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";//短信@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}@Overridepublic 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已經為我們提供好了這樣的方法:

//直接接受一個Uri public 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.nfchome.org")) });

其余不變。

上面這個功能還是比較有用的,例如我們往某些商品上貼上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)描述
01狀態字節,見下表(狀態字節編碼格式)
1nISO/IANA語言編碼。例如”en-US”,”fr-CA”。編碼格式是US-ASCII,長度(n)由狀態字節的后6位指定。
n+1m文本數據。編碼格式是UTF-8或UTF-16。編碼格式由狀態字節的前3位指定。

狀態字節編碼格式:

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

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

1.讀NFC標簽文本數據

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

<activityandroid:name=".ReadTextActivity"android:launchMode="singleTop"/> import android.content.Intent; import android.nfc.NdefMessage; import android.nfc.NdefRecord; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.nfc.tech.Ndef; import android.os.Parcelable; import android.os.Bundle; import android.widget.TextView; import android.widget.Toast;import java.util.Arrays;public class ReadTextActivity extends BaseNfcActivity {private TextView mNfcText;private String mTagText;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_read_text);mNfcText = (TextView) findViewById(R.id.tv_nfctext);}@Overridepublic void onNewIntent(Intent intent) {//1.獲取Tag對象Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);//2.獲取Ndef的實例Ndef ndef = Ndef.get(detectedTag);if (ndef!=null) {mTagText = ndef.getType() + "\nmaxsize:" + ndef.getMaxSize() + "bytes\n\n";readNfcTag(intent);mNfcText.setText(mTagText);}else{Toast.makeText(this, " 沒有獲取到 NDEF 實例! ", Toast.LENGTH_SHORT).show();}}/*** 讀取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格式*///判斷TNFif (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();}} }

2.寫NFC標簽文本數據

public class WriteTextActivity extends BaseNfcActivity {private String mText = "NFC-NewText-123";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_write_text);}@Overridepublic 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);//設置狀態字節編碼最高位數為0int 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偏移大小值描述
識別碼01byteUri識別碼用于存儲已知Uri的前綴
Uri字段1NUTF-8類型字符串用于存儲剩余字符串

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

十進制十六進制協議十進制十六進制協議
00x00N/A10x01http://www.
20x02https://www.30x03http://
40x04https://50x05tel:
60x06mailto:70x07ftp://anonymous:anonymous@
80x08ftp://ftp.90x09ftps://
100x0Asftp://110x0Bsmb://
120x0Cnfs://130x0Dftp://
140x0Edav://150x0Fnews:
160x10telnet://170x11imap:
180x12rtsp://190x13urn:
200x14pop:210x15sip:
220x16sips:230x17tftp:
240x18btspp://250x19btl2cap://
260x1Abtgoep://270x1Btcpobex://
280x1Cirdaobex://290x1Dfile://
300x1Eurn:epc:id:310x1Furn:epc:tag:
320x20urn:epc:pat:330x21urn:epc:raw:
340x22urn:epc:350x23urn: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的相關配置:

<activityandroid: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;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_read_uri);mNfcText = (TextView) findViewById(R.id.tv_nfctext);}@Overridepublic 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("UnknownTNF"+ tnf);}/*** 處理絕對的Uri* <p>* <p>* 沒有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";@Overridepublic 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數據了。

到這里,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 {@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_read_mu);}@Overridepublic 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{@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_write_mu);}@Overridepublic 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標簽開發深度解析到此就結束了!


?

以上為整理大神的文章內容 , 以下為我自己使用的工具類及使用 :??

以下內容分為 : xml 配置?, activity 部分 , NfcUtils?

xml --> androidManifest?

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.fn.fn_nfc"><!--todo 權限部分,必須配置--> <uses-permission android:name="android.permission.NFC" /> <uses-sdkandroid:minSdkVersion="10"android:targetSdkVersion="19" /> <uses-featureandroid:name="android.hardware.nfc"android:required="true" /><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"><!--todo 界面部分--><activityandroid:name=".MainActivity"android:launchMode="singleTask"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter><intent-filter><action android:name="android.nfc.action.TECH_DISCOVERED" /></intent-filter><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><meta-dataandroid:name="android.nfc.action.TECH_DISCOVERED"android:resource="@xml/nfc_tech_filter" /></activity></application></manifest>

xml --> res 下的 xml 文件夾下的文件?nfc_tech_filter.xml

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"><!-- 可以處理所有Android支持的NFC類型 --><tech-list><tech>android.nfc.tech.IsoDep</tech></tech-list><tech-list><tech>android.nfc.tech.NfcA</tech></tech-list><tech-list><tech>android.nfc.tech.NfcB</tech></tech-list><tech-list><tech>android.nfc.tech.NfcF</tech></tech-list><tech-list><tech>android.nfc.tech.NfcV</tech></tech-list><tech-list><tech>android.nfc.tech.Ndef</tech></tech-list><tech-list><tech>android.nfc.tech.NdefFormatable</tech></tech-list><tech-list><tech>android.nfc.tech.MifareUltralight</tech></tech-list><tech-list><tech>android.nfc.tech.MifareClassic</tech></tech-list> </resources>

NfcUtils 會有重復的功能 , 但可以相互參考 :

package com.fn.fn_nfc;import android.app.Activity; import android.app.PendingIntent; import android.content.Intent; import android.content.IntentFilter; import android.nfc.FormatException; import android.nfc.NdefMessage; import android.nfc.NdefRecord; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.nfc.tech.Ndef; import android.nfc.tech.NdefFormatable; import android.os.Parcelable; import android.provider.Settings; import android.util.Log; import android.widget.Toast;import java.io.IOException; import java.io.UnsupportedEncodingException;/*** Created by 77167 on 2018/10/15.*/public class NfcUtils {private static final String TAG = "NfcUtils";//nfcpublic static NfcAdapter mNfcAdapter;public static IntentFilter[] mIntentFilter = null;public static PendingIntent mPendingIntent = null;public static String[][] mTechList = null;private final Activity mActivity;/*** 構造函數,用于初始化nfc*/public NfcUtils(Activity activity) {mNfcAdapter = NfcCheck(activity);this.mActivity = activity;NfcInit(activity);}/*** 檢查NFC是否打開*/public static NfcAdapter NfcCheck(Activity activity) {NfcAdapter mNfcAdapter = NfcAdapter.getDefaultAdapter(activity);if (mNfcAdapter == null) {return null;} else {if (!mNfcAdapter.isEnabled()) {Intent setNfc = new Intent(Settings.ACTION_NFC_SETTINGS);activity.startActivity(setNfc);}}return mNfcAdapter;}/*** 初始化nfc設置*/public static void NfcInit(Activity activity) {mPendingIntent = PendingIntent.getActivity(activity, 0, new Intent(activity, activity.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);IntentFilter filter2 = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);try {filter.addDataType("*/*");} catch (IntentFilter.MalformedMimeTypeException e) {e.printStackTrace();}mIntentFilter = new IntentFilter[]{filter, filter2};mTechList = null;}/*** 往nfc寫入數據*/public static void writeNFCToTag(String data, Intent intent) throws IOException, FormatException {//1. 獲取Tag對象Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);//額外的//2. 判斷 NFC 標簽的數據類型Ndef ndef = Ndef.get(tag);ndef.connect();NdefRecord ndefRecord = null;//這里android sdk 版本, 只有大于 21 ,才能使用錄入功能if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {ndefRecord = NdefRecord.createTextRecord(null, data);}NdefRecord[] records = {ndefRecord};NdefMessage ndefMessage = new NdefMessage(records);//3. 寫入數據ndef.writeNdefMessage(ndefMessage);}/*** 讀取NFC的數據*/public static String readNFCFromTag(Intent intent) throws UnsupportedEncodingException {Parcelable[] rawArray = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);if (rawArray != null) {NdefMessage mNdefMsg = (NdefMessage) rawArray[0];NdefRecord mNdefRecord = mNdefMsg.getRecords()[0];if (mNdefRecord != null) {String readResult = new String(mNdefRecord.getPayload(), "UTF-8");return readResult;}}return "";}/*** 讀取nfcID*/public static String readNFCId(Intent intent) throws UnsupportedEncodingException {Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);String id = ByteArrayToHexString(tag.getId());return id;}/*** 將字節數組轉換為字符串*/private static String ByteArrayToHexString(byte[] inarray) {int i, j, in;String[] hex = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"};String out = "";for (j = 0; j < inarray.length; ++j) {in = (int) inarray[j] & 0xff;i = (in >> 4) & 0x0f;out += hex[i];i = in & 0x0f;out += hex[i];}return out;}/*** 讀取 NFC** @return*/public static String readNFCFromTag(Tag tag){if (tag == null) {return "";}//2.判斷NFC標簽的數據類型(通過Ndef.get方法)Ndef ndef = Ndef.get(tag);return "";}/*** 讀取 NFC TAG 方法*/public void readNFCTag(Intent intent){//1.獲取Tag對象Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);}/*** 往標簽寫數據的方法* https://blog.csdn.net/wolfking0608/article/details/72675180* @param tag*/public void writeNFCTag(Tag tag) {if (tag == null) {return;}//NdefMessage 描述NDEF格式的信息,實際上我們寫入NFC標簽的就是NdefMessage對象。//NdefRecord 描述NDEF信息的一個信息段,一個NdefMessage可能包含一個或者多個NdefRecord。NdefMessage ndefMessage = new NdefMessage(new NdefRecord[]{NdefRecord.createApplicationRecord(mActivity.getPackageName())});//轉換成字節獲得大小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);Log.i(TAG, "writeNFCTag: 寫入成功");} else {//當我們買回來的NFC標簽是沒有格式化的,或者沒有分區的執行此步//Ndef格式類NdefFormatable format = NdefFormatable.get(tag);//判斷是否獲得了NdefFormatable對象,有一些標簽是只讀的或者不允許格式化的if (format != null) {//連接format.connect();//格式化并將信息寫入標簽format.format(ndefMessage);Log.i(TAG, "writeNFCTag: 寫入成功");} else {Log.i(TAG, "writeNFCTag: 寫入失敗");}}} catch (Exception e) {}}}

Activity 只是在 onNewIntent(Intent intent) 讀取了 TagID?用 Toast 彈出 :

package com.fn.fn_nfc;import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.widget.Toast;import java.io.UnsupportedEncodingException;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initData();}private void initData() {//nfc初始化設置NfcUtils nfcUtils = new NfcUtils(this);}@Overrideprotected void onResume() {super.onResume();//開啟前臺調度系統NfcUtils.mNfcAdapter.enableForegroundDispatch(this, NfcUtils.mPendingIntent, NfcUtils.mIntentFilter, NfcUtils.mTechList);}@Overrideprotected void onPause() {super.onPause();//關閉前臺調度系統NfcUtils.mNfcAdapter.disableForegroundDispatch(this);}@Overrideprotected void onNewIntent(Intent intent) {super.onNewIntent(intent);//當該Activity接收到NFC標簽時,運行該方法//調用工具方法,讀取NFC數據try {String str = NfcUtils.readNFCId(intent);Log.i("fn_tag", "讀取到的標簽數據: " + str);Toast.makeText(this, "讀取到的標簽數據: " + str, Toast.LENGTH_SHORT).show();} catch (UnsupportedEncodingException e) {e.printStackTrace();}}}

?至此 , 可以直接運行了

總結

以上是生活随笔為你收集整理的Android NFC 标签读写读取快速开发教程 ( 整理来自 https://blog.csdn.net/wolfking0608/article/details/72675180 )的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。