本篇博客是為了解決以下的問(wèn)題:
- NFC是什么
- NFC的原理
- NFC工作模式和實(shí)用場(chǎng)景
- NFC相比于藍(lán)牙的優(yōu)勢(shì)
- Android讀取和寫入NDEF格式的NFC芯片
- Android讀取和寫入非NDEF格式的NFC芯片
NFC是什么
NFC是Near Field Communication的簡(jiǎn)稱断楷,意思為近距離無(wú)線通訊技術(shù),簡(jiǎn)單的說(shuō)是能夠在短距離之內(nèi)與兼容設(shè)備實(shí)現(xiàn)數(shù)據(jù)交換的技術(shù)。
NFC的原理
NFC是使用非接觸式射頻技術(shù)實(shí)現(xiàn)的數(shù)據(jù)交換技術(shù)掀鹅,在13.56MHz頻率20厘米距離內(nèi)進(jìn)行傳輸尊流,其傳輸速度有106 Kbit/秒鞍历、212 Kbit/秒或者424 Kbit/秒三種惹骂。
NFC工作模式和實(shí)用場(chǎng)景
NFC工作模式分為三種:
- 讀卡器模式贮预,指含有NFC芯片的標(biāo)簽徙邻,可以被支持NFC的手機(jī)或者專門讀卡器設(shè)備讀取或者寫入數(shù)據(jù)排嫌。一般可以用于巡更,用支持NFC的手機(jī)到定點(diǎn)區(qū)讀取NFC芯片的數(shù)據(jù)然后傳輸?shù)胶笈_(tái)表示已巡視了定點(diǎn)區(qū)缰犁。
- 仿真卡模式淳地,指含有IC卡的NFC芯片,可以使用支持NFC的手機(jī)或者NFC射頻器讀取出NFC芯片中IC卡的信息帅容。常見(jiàn)的由交通卡颇象,信用卡。
- 點(diǎn)對(duì)點(diǎn)模式并徘,指兩臺(tái)NFC設(shè)備之間互相都可以進(jìn)行數(shù)據(jù) 交換遣钳。
NFC相比于藍(lán)牙的優(yōu)勢(shì)
藍(lán)牙4.0相比于以前版本,功耗低麦乞,傳輸速度快蕴茴,可以在10m之內(nèi)使用劝评。
NFC技術(shù)優(yōu)勢(shì)是創(chuàng)建連接快,傳輸速度也不慢倦淀,存儲(chǔ)芯片中的數(shù)據(jù)可以加密安全性較高蒋畜,在20cm之內(nèi)使用。
Android讀取和寫入NDEF格式的NFC芯片
首先說(shuō)明NFC芯片可以根據(jù)廠家定制撞叽,一次性寫入然后只能讀姻成,簡(jiǎn)單的可讀可寫。目前Android SDK API主要支持NFC論壇標(biāo)準(zhǔn)(Forum Standard)愿棋,這種標(biāo)準(zhǔn)被稱為NDEF科展。
1.支持NFC功能,添加權(quán)限
<uses-permission android:name="android.permission.NFC" />
<!-- 要求當(dāng)前設(shè)備必須要有NFC芯片 -->
<uses-feature android:name="android.hardware.nfc" android:required="true" />
2.當(dāng)前Activity的清單文件中的設(shè)置
<activity
android:name="com.example.NFCActivity"
android:launchMode="singleTask" >
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
3.當(dāng)前Activity聲明周期的判斷
private NfcAdapter mNfcAdapter;
private PendingIntent mPendingIntent;
@Override
protected void onStart() {
super.onStart();
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
//一旦截獲NFC消息糠雨,就會(huì)通過(guò)PendingIntent調(diào)用窗口
mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()), 0);
}
/**
* 獲得焦點(diǎn)辛润,按鈕可以點(diǎn)擊
*/
@Override
public void onResume() {
super.onResume();
//后面兩個(gè)null,null設(shè)置處理優(yōu)于所有其他NFC的處理
if (mNfcAdapter != null)
mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
}
/**
* 暫停Activity,界面獲取焦點(diǎn)见秤,按鈕可以點(diǎn)擊
*/
@Override
public void onPause() {
super.onPause();
//恢復(fù)默認(rèn)狀態(tài)
if (mNfcAdapter != null)
mNfcAdapter.disableForegroundDispatch(this);
}
4.讀取NFC芯片中內(nèi)容
@Override
public void onNewIntent(Intent intent) {
//1.獲取Tag對(duì)象
Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
//2.獲取Ndef的實(shí)例
Ndef ndef = Ndef.get(detectedTag);
mTagText = ndef.getType() + "\nmaxsize:" + ndef.getMaxSize() + "bytes\n\n";
readNfcTag(intent);
mNfcText.setText(mTagText);
}
/**
* 讀取NFC標(biāo)簽文本數(shù)據(jù)
*/
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文本數(shù)據(jù)砂竖,從第三個(gè)字節(jié)開(kāi)始,后面的文本數(shù)據(jù)
* @param ndefRecord
* @return
*/
public static String parseTextRecord(NdefRecord ndefRecord) {
/**
* 判斷數(shù)據(jù)是否為NDEF格式
*/
//判斷TNF
if (ndefRecord.getTnf() != NdefRecord.TNF_WELL_KNOWN) {
return null;
}
//判斷可變的長(zhǎng)度的類型
if (!Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
return null;
}
try {
//獲得字節(jié)數(shù)組鹃答,然后進(jìn)行分析
byte[] payload = ndefRecord.getPayload();
//下面開(kāi)始NDEF文本數(shù)據(jù)第一個(gè)字節(jié)乎澄,狀態(tài)字節(jié)
//判斷文本是基于UTF-8還是UTF-16的,取第一個(gè)字節(jié)"位與"上16進(jìn)制的80测摔,16進(jìn)制的80也就是最高位是1置济,
//其他位都是0,所以進(jìn)行"位與"運(yùn)算后就會(huì)保留最高位
String textEncoding = ((payload[0] & 0x80) == 0) ? "UTF-8" : "UTF-16";
//3f最高兩位是0锋八,第六位是1浙于,所以進(jìn)行"位與"運(yùn)算后獲得第六位
int languageCodeLength = payload[0] & 0x3f;
//下面開(kāi)始NDEF文本數(shù)據(jù)第二個(gè)字節(jié),語(yǔ)言編碼
//獲得語(yǔ)言編碼
String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
//下面開(kāi)始NDEF文本數(shù)據(jù)后面的字節(jié)挟纱,解析出文本
String textRecord = new String(payload, languageCodeLength + 1,
payload.length - languageCodeLength - 1, textEncoding);
return textRecord;
} catch (Exception e) {
throw new IllegalArgumentException();
}
}
5.寫入NFC芯片
@Override
public void onNewIntent(Intent intent) {
if (mText == null)
return;
//獲取Tag對(duì)象
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();
}
}
/**
* 創(chuàng)建NDEF文本數(shù)據(jù)
* @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");
//將文本轉(zhuǎn)換為UTF-8格式
byte[] textBytes = text.getBytes(utfEncoding);
//設(shè)置狀態(tài)字節(jié)編碼最高位數(shù)為0
int utfBit = 0;
//定義狀態(tài)字節(jié)
char status = (char) (utfBit + langBytes.length);
byte[] data = new byte[1 + langBytes.length + textBytes.length];
//設(shè)置第一個(gè)狀態(tài)字節(jié)羞酗,先將狀態(tài)碼轉(zhuǎn)換成字節(jié)
data[0] = (byte) status;
//設(shè)置語(yǔ)言編碼,使用數(shù)組拷貝方法紊服,從0開(kāi)始拷貝到data中檀轨,拷貝到data的1到langBytes.length的位置
System.arraycopy(langBytes, 0, data, 1, langBytes.length);
//設(shè)置文本字節(jié),使用數(shù)組拷貝方法欺嗤,從0開(kāi)始拷貝到data中参萄,拷貝到data的1 + langBytes.length
//到textBytes.length的位置
System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length);
//通過(guò)字節(jié)傳入NdefRecord對(duì)象
//NdefRecord.RTD_TEXT:傳入類型 讀寫
NdefRecord ndefRecord = new NdefRecord(NdefRecord.TNF_WELL_KNOWN,
NdefRecord.RTD_TEXT, new byte[0], data);
return ndefRecord;
}
/**
* 寫數(shù)據(jù)
* @param ndefMessage 創(chuàng)建好的NDEF文本數(shù)據(jù)
* @param tag 標(biāo)簽
* @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;
}
核心的代碼已經(jīng)貼出,查看詳情的代碼煎饼,請(qǐng)點(diǎn)擊這里讹挎,已經(jīng)簡(jiǎn)化成工具類,可直接拷貝使用。
Android讀取和寫入非NDEF格式的NFC芯片
對(duì)于非NDEF格式數(shù)據(jù)的NFC芯片筒溃,其中的數(shù)據(jù)格式是自定義的马篮,但是存儲(chǔ)到芯片中都是以字節(jié)碼的形式。想深入學(xué)習(xí)的朋友可以铡羡,參考Github源碼积蔚,編譯生成的APK能夠讀取部分地區(qū)交通卡信息意鲸。
感謝以下知識(shí)的分享:
Android 高級(jí)開(kāi)發(fā)——NFC標(biāo)簽開(kāi)發(fā)深度解析
NFC百度百科