NFC簡介:前端
Near Field Communication 近場通訊,是一種數據傳輸技術。java
與wifi、藍牙、紅外線等數據傳輸技術的一個主要差別就是有效距離通常不能超過4cm。android
NFC支持3種工做模式:git
1.讀卡器模式;數組
2.仿真卡模式;app
3.點對點模式;ide
1.讀卡器模式:oop
經過NFC設備(支持NFC的Android手機)從帶有NFC芯片的標籤、貼紙、報紙、明信片等媒介讀取信息,或將數據寫到這些媒介中。大數據
2.仿真卡模式:ui
是將支持NFC的手機或其餘電子設備當成借記卡、信用卡、公交卡、門禁卡等IC卡使用;基本原理是將相應的IC卡中的信息(支付憑證)封裝成數據包存儲在支持NFC的手機中,在使用時還須要一個NFC射頻器(至關於刷傳統IC卡時使用的刷卡器),將手機靠近NFC射頻器,手機就會收到NFC射頻器發過來的信號,在經過一系列複雜的驗證後,將IC卡的相應信息傳入NFC射頻器,最後這些IC卡數據會傳入NFC射頻器鏈接的計算機,並進行相應的處理(如電子轉帳、開門等操做)。
3.點對點模式:
與藍牙、紅外差很少,能夠用於不一樣的NFC設備之間進行數據交換,只是NFC的點對點模式有效距離更短,不能超過4cm;可是若是兩個設備使用的都是Android4.2及以上版本,NFC會直接利用藍牙傳輸,這種技術被稱爲Android Beam,因此Android Beam傳輸數據的兩部設備不侷限於4cm以內。
基礎知識:
1.Android SDK API主要支持NFC論壇標準(Forum Standard),這種標準被稱爲NDEF(NFC Data Exchange Format,NFC數據交換格式);
2.Android SDK API支持以下三種NDEF數據的操做:
a.從NFC標籤讀取NDEF格式的數據;
b.向NFC標籤寫入NDEF格式的數據;
c.經過Android Beam技術將NDEF數據發送到另外一部NFC設備;
3.在一個NFC設備讀取NFC標籤或另外一個NFC設備中的數據以前會在0.1秒的時間以內創建NFC鏈接,而後數據會自動從被讀取一端流向讀取數據的一端;數據接收端會根據具體的數據格式和標籤類型調用相應的Activity(這種行爲也稱爲Tag Dispatch),這些Activity都須要定義Intent Filter,這些Intent Filter中就會指定不一樣的過濾機制,分爲三個級別,也稱爲NFC的三重過濾機制。
4.NDEF_DISCOVERED:
只過濾固定格式的NDEF數據。例如:純文本、指定協議(http、ftp、smb等)的URI等;
TECH_DISCOVERED:
當ACTION_NDEF_DISCOVERED指定的過濾機制沒法匹配Tag時,就會使用這種過濾機制進行匹配,這種過濾機制並非經過Tag中的數據格式進行匹配的,而是根據Tag支持的數據存儲格式進行匹配,所以這種過濾機制的範圍更廣;
TAG_DISCOVERED:
若是將NFC過濾機制當作if...else if...else語句的話,那麼這種過濾機制就至關於else部分,當前面兩種過濾機制都匹配失敗後,系統就會利用這種過濾機制來處理,這種過濾機制用來處理未識別的Tag(數據格式不對,並且Tag支持的格式也不匹配)。
5.Android系統會依次匹配NDEF_DISCOVERED、TECH_DISCOVERED和TAG_DISCOVERED;若是經過三重過濾機制仍然沒法匹配Tag,則什麼都不作;一般在成功匹配Tag後,Android設備會發出比較清脆的聲音,而未成功匹配Tag,就會發出比較沉悶的聲音。
此過程的處理流程以下圖所示:
6.在manifest文件中須要設置的部分有:
設置權限:
<uses-permission android:name="android.permission.NFC" />
限制Android版本:
android:minSdkVersion="14"
限制安裝的設備:
<uses-feature android:name="android.hardware.nfc" android:required="true" />
設置Activity的Intent Filter,好比設置爲三種過濾機制的一種:
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
</intent-filter>
接下來,咱們來第一個例子,這個例子是屬於讀卡器模式,從NFC芯片中讀取和寫入數據。
它的manifest文件內容以下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.r8c.nfc_demo"
android:versionCode="110"
android:versionName="1.1.0" >
<uses-sdk
android:minSdkVersion="15"
android:targetSdkVersion="17" />
<!-- NFC權限聲明 -->
<uses-permission android:name="android.permission.NFC" />
<uses-feature
android:name="android.hardware.nfc"
android:required="true" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.r8c.nfc_demo.NfcDemoActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:label="@string/app_name"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- TECH_DISCOVERED類型的nfc -->
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
</intent-filter>
<!-- 後設資源 調用本身創建的文件夾xml中的文件 -->
<meta-data
android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/nfc_tech_filter" />
</activity>
</application>
</manifest>
它的Activity的內容以下,包括讀取、寫入、刪除三大功能:(其中刪除功能是經過寫入空值來實現的)
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.MifareUltralight;
import android.nfc.tech.Ndef;
import android.nfc.tech.NfcA;
import android.os.Bundle;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class NfcDemoActivity extends Activity implements OnClickListener {
// NFC適配器
private NfcAdapter nfcAdapter = null;
// 傳達意圖
private PendingIntent pi = null;
// 濾掉組件沒法響應和處理的Intent
private IntentFilter tagDetected = null;
// 文本控件
private TextView promt = null;
// 是否支持NFC功能的標籤
private boolean isNFC_support = false;
// 讀、寫、刪按鈕控件
private Button readBtn, writeBtn, deleteBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nfc_demo);
setupViews();
initNFCData();
}
@Override
protected void onResume() {
super.onResume();
if (isNFC_support == false) {
// 若是設備不支持NFC或者NFC功能沒開啓,就return掉
return;
}
// 開始監聽NFC設備是否鏈接
startNFC_Listener();
if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(this.getIntent()
.getAction())) {
// 注意這個if中的代碼幾乎不會進來,由於剛剛在上一行代碼開啓了監聽NFC鏈接,下一行代碼立刻就收到了NFC鏈接的intent,這種概率很小
// 處理該intent
processIntent(this.getIntent());
}
}
@Override
protected void onPause() {
super.onPause();
if (isNFC_support == true) {
// 當前Activity若是不在手機的最前端,就中止NFC設備鏈接的監聽
stopNFC_Listener();
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// 當前app正在前端界面運行,這個時候有intent發送過來,那麼系統就會調用onNewIntent回調方法,將intent傳送過來
// 咱們只須要在這裏檢驗這個intent是不是NFC相關的intent,若是是,就調用處理方法
if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) {
processIntent(intent);
}
}
@Override
public void onClick(View v) {
// 點擊讀按鈕後
if (v.getId() == R.id.read_btn) {
try {
String content = read(tagFromIntent);
if (content != null && !content.equals("")) {
promt.setText(promt.getText() + "nfc標籤內容:\n" + content
+ "\n");
} else {
promt.setText(promt.getText() + "nfc標籤內容:\n" + "內容爲空\n");
}
} catch (IOException e) {
promt.setText(promt.getText() + "錯誤:" + e.getMessage() + "\n");
Log.e("myonclick", "讀取nfc異常", e);
} catch (FormatException e) {
promt.setText(promt.getText() + "錯誤:" + e.getMessage() + "\n");
Log.e("myonclick", "讀取nfc異常", e);
}
// 點擊寫後寫入
} else if (v.getId() == R.id.write_btn) {
try {
write(tagFromIntent);
} catch (IOException e) {
promt.setText(promt.getText() + "錯誤:" + e.getMessage() + "\n");
Log.e("myonclick", "寫nfc異常", e);
} catch (FormatException e) {
promt.setText(promt.getText() + "錯誤:" + e.getMessage() + "\n");
Log.e("myonclick", "寫nfc異常", e);
}
} else if (v.getId() == R.id.delete_btn) {
try {
delete(tagFromIntent);
} catch (IOException e) {
promt.setText(promt.getText() + "錯誤:" + e.getMessage() + "\n");
Log.e("myonclick", "刪除nfc異常", e);
} catch (FormatException e) {
promt.setText(promt.getText() + "錯誤:" + e.getMessage() + "\n");
Log.e("myonclick", "刪除nfc異常", e);
}
}
}
private void setupViews() {
// 控件的綁定
promt = (TextView) findViewById(R.id.promt);
readBtn = (Button) findViewById(R.id.read_btn);
writeBtn = (Button) findViewById(R.id.write_btn);
deleteBtn = (Button) findViewById(R.id.delete_btn);
// 給文本控件賦值初始文本
promt.setText("等待RFID標籤");
// 監聽讀、寫、刪按鈕控件
readBtn.setOnClickListener(this);
writeBtn.setOnClickListener(this);
deleteBtn.setOnClickListener(this);
}
private void initNFCData() {
// 初始化設備支持NFC功能
isNFC_support = true;
// 獲得默認nfc適配器
nfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
// 提示信息定義
String metaInfo = "";
// 斷定設備是否支持NFC或啓動NFC
if (nfcAdapter == null) {
metaInfo = "設備不支持NFC!";
Toast.makeText(this, metaInfo, Toast.LENGTH_SHORT).show();
isNFC_support = false;
}
if (!nfcAdapter.isEnabled()) {
metaInfo = "請在系統設置中先啓用NFC功能!";
Toast.makeText(this, metaInfo, Toast.LENGTH_SHORT).show();
isNFC_support = false;
}
if (isNFC_support == true) {
init_NFC();
} else {
promt.setTextColor(Color.RED);
promt.setText(metaInfo);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.nfc_demo, menu);
return true;
}
// 字符序列轉換爲16進制字符串
private String bytesToHexString(byte[] src) {
return bytesToHexString(src, true);
}
private String bytesToHexString(byte[] src, boolean isPrefix) {
StringBuilder stringBuilder = new StringBuilder();
if (isPrefix == true) {
stringBuilder.append("0x");
}
if (src == null || src.length <= 0) {
return null;
}
char[] buffer = new char[2];
for (int i = 0; i < src.length; i++) {
buffer[0] = Character.toUpperCase(Character.forDigit(
(src[i] >>> 4) & 0x0F, 16));
buffer[1] = Character.toUpperCase(Character.forDigit(src[i] & 0x0F,
16));
System.out.println(buffer);
stringBuilder.append(buffer);
}
return stringBuilder.toString();
}
private Tag tagFromIntent;
/**
* Parses the NDEF Message from the intent and prints to the TextView
*/
public void processIntent(Intent intent) {
if (isNFC_support == false)
return;
// 取出封裝在intent中的TAG
tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
promt.setTextColor(Color.BLUE);
String metaInfo = "";
metaInfo += "卡片ID:" + bytesToHexString(tagFromIntent.getId()) + "\n";
Toast.makeText(this, "找到卡片", Toast.LENGTH_SHORT).show();
// Tech List
String prefix = "android.nfc.tech.";
String[] techList = tagFromIntent.getTechList();
//分析NFC卡的類型: Mifare Classic/UltraLight Info
String CardType = "";
for (int i = 0; i < techList.length; i++) {
if (techList[i].equals(NfcA.class.getName())) {
// 讀取TAG
NfcA mfc = NfcA.get(tagFromIntent);
try {
if ("".equals(CardType))
CardType = "MifareClassic卡片類型 \n 不支持NDEF消息 \n";
} catch (Exception e) {
e.printStackTrace();
}
} else if (techList[i].equals(MifareUltralight.class.getName())) {
MifareUltralight mifareUlTag = MifareUltralight
.get(tagFromIntent);
String lightType = "";
// Type Info
switch (mifareUlTag.getType()) {
case MifareUltralight.TYPE_ULTRALIGHT:
lightType = "Ultralight";
break;
case MifareUltralight.TYPE_ULTRALIGHT_C:
lightType = "Ultralight C";
break;
}
CardType = lightType + "卡片類型\n";
Ndef ndef = Ndef.get(tagFromIntent);
CardType += "最大數據尺寸:" + ndef.getMaxSize() + "\n";
}
}
metaInfo += CardType;
promt.setText(metaInfo);
}
// 讀取方法
private String read(Tag tag) throws IOException, FormatException {
if (tag != null) {
//解析Tag獲取到NDEF實例
Ndef ndef = Ndef.get(tag);
//打開鏈接
ndef.connect();
//獲取NDEF消息
NdefMessage message = ndef.getNdefMessage();
//將消息轉換成字節數組
byte[] data = message.toByteArray();
//將字節數組轉換成字符串
String str = new String(data, Charset.forName("UTF-8"));
//關閉鏈接
ndef.close();
return str;
} else {
Toast.makeText(NfcDemoActivity.this, "設備與nfc卡鏈接斷開,請從新鏈接...",
Toast.LENGTH_SHORT).show();
}
return null;
}
// 寫入方法
private void write(Tag tag) throws IOException, FormatException {
if (tag != null) {
//新建NdefRecord數組,本例中數組只有一個元素
NdefRecord[] records = { createRecord() };
//新建一個NdefMessage實例
NdefMessage message = new NdefMessage(records);
// 解析TAG獲取到NDEF實例
Ndef ndef = Ndef.get(tag);
// 打開鏈接
ndef.connect();
// 寫入NDEF信息
ndef.writeNdefMessage(message);
// 關閉鏈接
ndef.close();
promt.setText(promt.getText() + "寫入數據成功!" + "\n");
} else {
Toast.makeText(NfcDemoActivity.this, "設備與nfc卡鏈接斷開,請從新鏈接...",
Toast.LENGTH_SHORT).show();
}
}
// 刪除方法
private void delete(Tag tag) throws IOException, FormatException {
if (tag != null) {
//新建一個裏面無任何信息的NdefRecord實例
NdefRecord nullNdefRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA,
new byte[] {}, new byte[] {}, new byte[] {});
NdefRecord[] records = { nullNdefRecord };
NdefMessage message = new NdefMessage(records);
// 解析TAG獲取到NDEF實例
Ndef ndef = Ndef.get(tag);
// 打開鏈接
ndef.connect();
// 寫入信息
ndef.writeNdefMessage(message);
// 關閉鏈接
ndef.close();
promt.setText(promt.getText() + "刪除數據成功!" + "\n");
} else {
Toast.makeText(NfcDemoActivity.this, "設備與nfc卡鏈接斷開,請從新鏈接...",
Toast.LENGTH_SHORT).show();
}
}
//返回一個NdefRecord實例
private NdefRecord createRecord() throws UnsupportedEncodingException {
//組裝字符串,準備好你要寫入的信息
String msg = "BEGIN:VCARD\n" + "VERSION:2.1\n" + "中國湖北省武漢市\n"
+ "武漢大學計算機學院\n" + "END:VCARD";
//將字符串轉換成字節數組
byte[] textBytes = msg.getBytes();
//將字節數組封裝到一個NdefRecord實例中去
NdefRecord textRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA,
"text/x-vCard".getBytes(), new byte[] {}, textBytes);
return textRecord;
}
private MediaPlayer ring() throws Exception, IOException {
// TODO Auto-generated method stub
Uri alert = RingtoneManager
.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
MediaPlayer player = new MediaPlayer();
player.setDataSource(this, alert);
final AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
if (audioManager.getStreamVolume(AudioManager.STREAM_NOTIFICATION) != 0) {
player.setAudioStreamType(AudioManager.STREAM_NOTIFICATION);
player.setLooping(false);
player.prepare();
player.start();
}
return player;
}
private void startNFC_Listener() {
// 開始監聽NFC設備是否鏈接,若是鏈接就發pi意圖
nfcAdapter.enableForegroundDispatch(this, pi,
new IntentFilter[] { tagDetected }, null);
}
private void stopNFC_Listener() {
// 中止監聽NFC設備是否鏈接
nfcAdapter.disableForegroundDispatch(this);
}
private void init_NFC() {
// 初始化PendingIntent,當有NFC設備鏈接上的時候,就交給當前Activity處理
pi = PendingIntent.getActivity(this, 0, new Intent(this, getClass())
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
// 新建IntentFilter,使用的是第二種的過濾機制
tagDetected = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
tagDetected.addCategory(Intent.CATEGORY_DEFAULT);
}
}
下面是該示例的完整源碼下載連接: