整個篇幅 上面為代碼,最后面為介紹,如果想直接看我遇到過的坑,請直接拉到最后
目錄
一、先來個NDEF寫入代碼:主要在Activity中的生命周期中呼叫
1??Activity 代碼
2??工具類代碼
3??Manifest中的 activity注冊代碼
4??TECH_DISCOVERED resource nfc_tech_filter
5??工具類
上面為 Nfc 的所有型別的讀取 以及NDEF型別的寫入,沒有其他型別的寫入,暫時沒有加密,后期會考慮新寫一篇來介紹,
二、注意事項
1、Android 如果想要啟動一個應用,并且到達指定界面,需要注意NdefRecord添加的先后順序,因為他會依次執行,type 必須是小寫,否則可能過濾失效,跳往啟動頁
2、IOS 不能主動識別鏈接之外的NdefRecord(除非主動去獲取),所以NdefRecord[]需要添加一個鏈接, iOS跳往外鏈去識別(添加到最后)
3、如果想要給 NdefRecord添加Id 只能自己去New NdefRecord但是又想達成人家代碼的效果
一、先來個NDEF寫入代碼:主要在Activity中的生命周期中呼叫
1??Activity 代碼
package com.jiao.demo.nfc;
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.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.IsoDep;
import android.nfc.tech.MifareClassic;
import android.nfc.tech.MifareUltralight;
import android.nfc.tech.Ndef;
import android.nfc.tech.NfcA;
import android.nfc.tech.NfcB;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.haier.hailicommontlib.mvp.model.utils.LogUtil;
import com.haier.hailicommontlib.mvp.model.utils.ToastUtil;
import com.jiao.demo.nfc.model.NfcUtil;
import com.jiao.demo.nfc.model.StringUtil;
import java.io.IOException;
/**
* @author: jiaojunfeng
* @date: 2020/11/6
* @describe: NFC demo
*/
public class NFCMainActivity 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;
private Button btRead;
private Button btWrite;
private Button btDelete;
private TextView tvContent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_n_f_c_main);
initView();
initNFCData();
}
@Override
protected void onResume() {
super.onResume();
if (!isNFC_support) {
// 如果設備不支持NFC或者NFC功能沒開啟,就return掉
ToastUtil.showLongToast(this, "NFC功能受限,本設備不支持或未開啟");
return;
}
// 開始監聽NFC設備是否連接
startNFC_Listener();
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(this.getIntent()
.getAction())) {
// 處理該intent
processIntent(this.getIntent());
}
}
@Override
protected void onPause() {
super.onPause();
if (isNFC_support) {
// 當前Activity如果不在手機的最前端,就停止NFC設備連接的監聽
stopNFC_Listener();
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// 當前app正在前端界面運行,這個時候有intent發送過來,那么系統就會呼叫onNewIntent回呼方法,將intent傳送過來
// 我們只需要在這里檢驗這個intent是否是NFC相關的intent,如果是,就呼叫處理方法
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
processIntent(intent);
}
}
@Override
public void onClick(View v) {
// 點擊讀按鈕后
if (v.getId() == R.id.bt_read) {
processIntent(getIntent());
// 點擊寫后寫入
} else if (v.getId() == R.id.bt_write) {
try {
String content = NfcUtil.writeNdef(tagFromIntent);
settext(content);
} catch (Exception e) {
settext("錯誤:" + e.getMessage());
Log.e("myonclick", "寫nfc例外", e);
}
} else if (v.getId() == R.id.bt_delete) {
try {
delete(tagFromIntent);
} catch (IOException e) {
settext("錯誤:" + e.getMessage());
} catch (FormatException e) {
settext("錯誤:" + e.getMessage());
Log.e("myonclick", "洗掉nfc例外", e);
}
}
}
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) {
init_NFC();
} else {
promt.setTextColor(Color.RED);
settext(metaInfo);
}
}
private Tag tagFromIntent;
/**
* Parses the NDEF Message from the intent and prints to the TextView
*/
public void processIntent(Intent intent) {
if (!isNFC_support || intent == null) {
ToastUtil.showLongToast(this, "不支持NFC 或資料Intent==NULL");
return;
}
// 取出封裝在intent中的TAG
tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (tagFromIntent == null) {
Toast.makeText(this, "TAG為NULL", Toast.LENGTH_SHORT).show();
return;
}
promt.setTextColor(Color.BLUE);
String metaInfo = "";
metaInfo += "卡片ID:" + StringUtil.bytesToHexString(tagFromIntent.getId()) + "\n";
Toast.makeText(this, "找到卡片", Toast.LENGTH_SHORT).show();
// Tech List
String[] techList = tagFromIntent.getTechList();
//分析NFC卡的型別: Mifare Classic/UltraLight Info
String CardType = "\n\t" + techList.length + "\n\t";
for (String s : techList) {
LogUtil.I(TAG, "" + s);
if (s.equals(NfcA.class.getName())) {
// 讀取TAG
NfcA mfc = NfcA.get(tagFromIntent);
settext(mfc.getTag().toString());
try {
if ("".equals(CardType))
CardType = "MifareClassic卡片型別 \n 不支持NDEF訊息 \n";
} catch (Exception e) {
e.printStackTrace();
}
} else if (s.equals(NfcB.class.getName())) {
try {
NfcB nfcB = NfcB.get(tagFromIntent);
nfcB.connect();
} catch (IOException e) {
e.printStackTrace();
}
CardType = "身份證";
} else if (s.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";
} else if (s.equals(MifareClassic.class.getName())) {
try {
CardType = NfcUtil.readCard(tagFromIntent, NfcUtil.MIFARECLASSIC_CARD);
} catch (IOException | FormatException e) {
CardType += "MifareClassic 錯誤" + e.toString();
e.printStackTrace();
}
} else if (s.equals(IsoDep.class.getName())) {
try {
CardType = NfcUtil.readCard(tagFromIntent, NfcUtil.ISO_DEP_CARD);
} catch (IOException | FormatException e) {
CardType += "IsoDep 錯誤" + e.toString();
e.printStackTrace();
}
} else if (s.equals(Ndef.class.getName())) {
try {
CardType = NfcUtil.readCard(intent, tagFromIntent, NfcUtil.NDEF_CARD);
} catch (IOException | FormatException e) {
CardType += "Ndef 錯誤" + e.toString();
e.printStackTrace();
}
}
}
metaInfo += CardType;
settext(metaInfo);
}
private String TAG = "NFCMAINac";
// 洗掉方法
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();
settext("洗掉資料成功");
} else {
settext("設備與nfc卡連接斷開,請重新連接...");
}
}
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("haili.DEFAULT");
}
private void initView() {
btRead = (Button) findViewById(R.id.bt_read);
btWrite = (Button) findViewById(R.id.bt_write);
btDelete = (Button) findViewById(R.id.bt_delete);
tvContent = (TextView) findViewById(R.id.tv_content);
btDelete = (Button) findViewById(R.id.bt_delete_log);
// 控制元件的系結
promt = (TextView) findViewById(R.id.tv_content);
readBtn = (Button) findViewById(R.id.bt_read);
writeBtn = (Button) findViewById(R.id.bt_write);
deleteBtn = (Button) findViewById(R.id.bt_delete);
// 給文本控制元件賦值初始文本
promt.setText("等待RFID標簽");
// 監聽讀、寫、刪按鈕控制元件
readBtn.setOnClickListener(this);
writeBtn.setOnClickListener(this);
deleteBtn.setOnClickListener(this);
btDelete.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
promt.setText("");
}
});
}
public void settext(String message) {
promt.setText(promt.getText() + " \n\t" + message);
}
}
2??工具類代碼
package com.jiao.demo.nfc.model;
import android.content.Intent;
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.IsoDep;
import android.nfc.tech.MifareClassic;
import android.nfc.tech.Ndef;
import android.nfc.tech.NfcB;
import android.os.Parcelable;
import com.haier.hailicommontlib.mvp.model.utils.LogUtil;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* @author: jjf
* @date: 2020/11/9
* @describe:
*/
public class NfcUtil {
private static String TAG = "NfcUtil";
public static String NULL_DATA = "\n\tNdef is null...\n\t";
public static String DEVICE_CONNENT_ERROR = "\n\t設備與nfc卡連接斷開,請重新連接...\n\t";
public static final int NFCA_CARD = 11;
public static final int NFCB_CARD = 12;
public static final int NDEF_CARD = 21;
public static final int MIFARECLASSIC_CARD = 31;
public static final int ISO_DEP_CARD = 41;
public static String readCard(Tag tag, int cartType) throws IOException, FormatException {
switch (cartType) {
case NFCA_CARD:
case NFCB_CARD:
return readNfcB(tag);
case NDEF_CARD:
return readNdefRecordPayload(tag);
case MIFARECLASSIC_CARD:
return readMifareClassic(tag);
case ISO_DEP_CARD:
return IsoDep(tag);
}
return "未發現的芯片型別";
}
/**
* 讀取Ndef 資訊 根據Id 獲取指定NdefRecord 的附加引數
* @param tag
* @param id 指定NdefRecord (訊息錄入時 設定的Id)
* @return
* @throws IOException
* @throws FormatException
*/
public static String readNdef(Tag tag, String id) throws IOException, FormatException {
//決議Tag獲取到NDEF實體
Ndef ndef = Ndef.get(tag);
if (ndef == null) {
return NULL_DATA;
}
//打開連接
if (!ndef.isConnected()) {
ndef.connect();
}
//獲取NDEF訊息
NdefMessage message = ndef.getNdefMessage();
if (message == null) {
ndef.close();
return "NdefMessage==null0";
}
NdefRecord[] ndefRecord = message.getRecords();
for (NdefRecord ndefRecord1 : ndefRecord) {
byte[] payload = ndefRecord1.getPayload();
if (payload != null) {
String str = new String(payload, StandardCharsets.UTF_8);
System.out.println(" " + str);
}
}
//將訊息轉換成位元組陣列
byte[] data = message.toByteArray();
//將位元組陣列轉換成字串
String str = new String(data, StandardCharsets.UTF_8);
//關閉連接
ndef.close();
System.out.println(" " + str);
return str;
}
// 讀取Ndef 引數,所有NdefRecord 附帶引數拼接一起回傳
public static String readNdefRecordPayload(Tag tag) {
StringBuilder str = new StringBuilder();
Ndef ndef = null;
try {
//決議Tag獲取到NDEF實體
if (tag == null) {
return "Tag==null";
}
ndef = Ndef.get(tag);
if (ndef == null) {
return NfcUtil.NULL_DATA;
}
//打開連接
ndef.connect();
//獲取NDEF訊息
NdefMessage message = ndef.getNdefMessage();
if (message == null) {
ndef.close();
return "NdefMessage==null";
}
NdefRecord[] ndefRecord = message.getRecords();
for (NdefRecord ndefRecord1 : ndefRecord) {
byte[] payload = ndefRecord1.getPayload();
if (payload != null) {
if (str.toString().length() != 0) {
str.append("&");
}
str.append(new String(payload, StandardCharsets.UTF_8));
}
}
//關閉連接
ndef.close();
return str.toString();
} catch (Exception e) {
LogUtil.I("Base", "NfcUtil " + e.toString());
e.printStackTrace();
} finally {
if (ndef != null) {
try {
ndef.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return str.toString();
}
// 讀取NfcB
public static String readNfcB(Tag tag) throws IOException, FormatException {
if (tag != null) {
//決議Tag獲取到NDEF實體
NfcB ndef = NfcB.get(tag);
if (ndef == null) {
return NULL_DATA;
}
try {
//打開連接
ndef.connect();
} catch (Exception e) {
e.printStackTrace();
}
//獲取NfcB訊息
byte[] message = ndef.getProtocolInfo();
for (int i = 0; message != null && i < message.length; i++) {
LogUtil.I(TAG, "message[" + i + "]" + message[i]);
}
//將位元組陣列轉換成字串
String str = new String(message, StandardCharsets.UTF_8);
//關閉連接
ndef.close();
return str;
} else {
return DEVICE_CONNENT_ERROR;
}
}
// Ndef 訊息寫入方法
public static String writeNdef(Tag tag) throws Exception {
if (tag != null) {
//新建NdefRecord陣列,本例中陣列只有一個元素
NdefRecord[] records = createNdefRecordByNFCUri("deviceId=123456");
//新建一個NdefMessage實體
NdefMessage message = new NdefMessage(records);
// 決議TAG獲取到NDEF實體
Ndef ndef = Ndef.get(tag);
// 打開連接
if (!ndef.isConnected()) {
ndef.connect();
}
// 寫入NDEF資訊
ndef.writeNdefMessage(message);
// 關閉連接
ndef.close();
return "寫入資料成功!";
} else {
return DEVICE_CONNENT_ERROR;
}
}
// Ndef簡單文本寫入
public static NdefRecord createRecord() {
//組裝字串,準備好你要寫入的資訊
String msg = "BEGIN:HAHA\n" + "VERSION:1.0\n" + "DEVICE_ID:123456\n"
+ "hl_company\n" + "END:HAHA";
//將字串轉換成位元組陣列
byte[] textBytes = msg.getBytes();
//將位元組陣列封裝到一個NdefRecord實體中去
NdefRecord textRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA,
"text/*".getBytes(), new byte[]{}, textBytes);
return textRecord;
}
/**
* Ndef 創建訊息(錄入NFC)
*
* @param content
* @return
*/
public static NdefRecord[] createNdefRecordByNFCUri(String content) {
//訊息ID
String id = "hailiWasher01";
//這一段是對應的想要打開的Activity中添加的意圖過濾引數pathPrefix 為自定義(前面要有斜杠),其余為固定寫法
//<intent-filter>
// <action android:name="android.nfc.action.NDEF_DISCOVERED" />
// <category android:name="android.intent.category.DEFAULT" />
// <data android:scheme="vnd.android.nfc"
// android:host="ext"
// android:pathPrefix="/com.jiao.demo.nfc.nfcmainactivity:nfc"/>
//</intent-filter>
return new NdefRecord[]{
//打開App , ,第一個引數跟第二個引數就是Manifest中的android:pathPrefix,第三個引數是自己添加的引數
createExternal("com.jiao.demo.nfc.nfcmainactivity", id, "nfc", content.getBytes()),
//想要打開的應用包名(如果是組件化專案,包名必須是主專案的包名,也就是說,模塊的build.gradle 中頂部代碼為 apply plugin: 'com.android.application')
//如果沒有安裝這個應用,則會跳往應用市場
NdefRecord.createApplicationRecord("com.haier.haierwashertopspeed"),
//打開的鏈接,這里主要用于IOS 識別
NdefRecord.createUri("https://www.baidu.com" + "?" + content),
};
}
/**
* 重寫NdefRecord方法 添加Id
* {@link NdefRecord#createExternal(String, String, byte[])}
*
* @param domain domain-name of issuing organization (自定義的,Activity路徑 與 @param type 一起組成引數pathPrefix)
* @param type domain-specific type of data (自定義的,Activity路徑 與 @param domain 一起組成引數pathPrefix)
* @param id 訊息Id
* @param data 自添加資料 可以用來追加引數
* @return
*/
public static NdefRecord createExternal(String domain, String id, String type, byte[] data) {
if (domain == null) throw new NullPointerException("domain is null");
if (type == null) throw new NullPointerException("type is null");
domain = domain.trim().toLowerCase(Locale.ROOT);
type = type.trim().toLowerCase(Locale.ROOT);
if (domain.length() == 0) throw new IllegalArgumentException("domain is empty");
if (type.length() == 0) throw new IllegalArgumentException("type is empty");
byte[] byteDomain = domain.getBytes(StandardCharsets.UTF_8);
byte[] byteType = type.getBytes(StandardCharsets.UTF_8);
byte[] b = new byte[byteDomain.length + 1 + byteType.length];
System.arraycopy(byteDomain, 0, b, 0, byteDomain.length);
b[byteDomain.length] = ':';
System.arraycopy(byteType, 0, b, byteDomain.length + 1, byteType.length);
return new NdefRecord(NdefRecord.TNF_EXTERNAL_TYPE, b, id == null ? null : id.getBytes(), data);
}
//讀取EXTRA_NDEF_MESSAGES內容:
public static String readFromTag(Intent intent) {
Parcelable[] rawArray = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefMessage mNdefMsg = (NdefMessage) rawArray[0];
NdefRecord mNdefRecord = mNdefMsg.getRecords()[0];
try {
if (mNdefRecord != null) {
return new String(mNdefRecord.getPayload(), "UTF-8");
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return "";
}
/**
* 讀取MifareClassic 型別
*
* @param tag
* @return
*/
public static String readMifareClassic(Tag tag) {
//取出封裝在intent中的TAG
String CardId = StringUtil.bytesToHexString(tag.getId());
String metaInfo = "";
metaInfo += "卡片ID:" + CardId;
for (String tech : tag.getTechList()) {
System.out.println(tech);
}
boolean auth;
//讀取TAG
MifareClassic mfc = MifareClassic.get(tag);
try {
//Enable I/O operations to the tag from this TagTechnology object.
if (!mfc.isConnected()) {
mfc.connect();
}
int type = mfc.getType();//獲取TAG的型別
int sectorCount = mfc.getSectorCount();//獲取TAG中包含的扇區數
String typeS = "";
switch (type) {
case MifareClassic.TYPE_CLASSIC:
typeS = "TYPE_CLASSIC";
break;
case MifareClassic.TYPE_PLUS:
typeS = "TYPE_PLUS";
break;
case MifareClassic.TYPE_PRO:
typeS = "TYPE_PRO";
break;
case MifareClassic.TYPE_UNKNOWN:
typeS = "TYPE_UNKNOWN";
break;
}
metaInfo += "\n卡片型別:" + typeS + "\n共" + sectorCount + "個扇區\n共"
+ mfc.getBlockCount() + "個塊\n存盤空間: " + mfc.getSize() + "B\n";
for (int j = 0; j < sectorCount; j++) {
//Authenticate a sector with key A.
auth = mfc.authenticateSectorWithKeyA(j,
MifareClassic.KEY_DEFAULT);
int bCount;
int bIndex;
if (auth) {
metaInfo += "Sector " + j + ":驗證成功\n";
// 讀取扇區中的塊
bCount = mfc.getBlockCountInSector(j);
bIndex = mfc.sectorToBlock(j);
for (int i = 0; i < bCount; i++) {
byte[] data = mfc.readBlock(bIndex);
metaInfo += "Block " + bIndex + " : "
+ StringUtil.bytesToHexString(data) + "\n";
bIndex++;
}
} else {
metaInfo += "Sector " + j + ":驗證失敗\n";
}
}
return metaInfo;
//Toast.makeText(this, metaInfo, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
return "讀取錯誤:" + e.toString();
}
}
/**
* 讀取IsoDep
*
* @param tag
* @return
*/
public static String IsoDep(Tag tag) {
IsoDep isoDep = IsoDep.get(tag);
String str = "";
try {
isoDep.connect(); // 連接
if (isoDep.isConnected()) {
LogUtil.D(TAG, "isoDep.isConnected"); // 判斷是否連接上
// 1.select PSF (1PAY.SYS.DDF01)
// 選擇支付系統檔案,它的名字是1PAY.SYS.DDF01,
byte[] DFN_PSE = {(byte) '1', (byte) 'P', (byte) 'A', (byte) 'Y', (byte) '.', (byte) 'S', (byte) 'Y', (byte) 'S', (byte) '.', (byte) 'D', (byte) 'D', (byte) 'F', (byte) '0', (byte) '1',};
byte[] payFile = isoDep.transceive(getSelectCommand(DFN_PSE));
String EN_CODE_TYPE = "GBK";
String payFileStr = new String(payFile, EN_CODE_TYPE);
str += "\n\t" + "支付系統:" + payFileStr;
// 2.選擇公交卡應用的名稱
byte[] DFN_SRV = {(byte) 0xA0, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x86, (byte) 0x98, (byte) 0x07, (byte) 0x01,};
byte[] card_name = isoDep.transceive(getSelectCommand(DFN_SRV));
String card_nameStr = new String(card_name, EN_CODE_TYPE);
str += "\n\t" + "公交卡應用的名稱:" + card_nameStr;
// 3.讀取余額
byte[] ReadMoney = {(byte) 0x80, // CLA Class
(byte) 0x5C, // INS Instruction
(byte) 0x00, // P1 Parameter 1
(byte) 0x02, // P2 Parameter 2
(byte) 0x04, // Le
};
byte[] Money = isoDep.transceive(ReadMoney);
String MoneyStr = new String(card_name, EN_CODE_TYPE);
str += "\n\t" + "余額1:" + MoneyStr;
if (Money != null && Money.length > 4) {
int cash = byteToInt(Money, 4);
float ba = cash / 100.0f;
str += "\n\t" + "余額2:" + ba;
}
// 4.讀取所有交易記錄
byte[] ReadRecord = {(byte) 0x00, // CLA Class
(byte) 0xB2, // INS Instruction
(byte) 0x01, // P1 Parameter 1
(byte) 0xC5, // P2 Parameter 2
(byte) 0x00, // Le
};
byte[] Records = isoDep.transceive(ReadRecord);
if (Records != null && Records.length > 4) {
int cash = byteToInt(Records, 4);
float ba = cash / 100.0f;
str += "\n\t" + "總消費記錄:" + ba;
}
ArrayList<byte[]> ret = parseRecords(Records);
List<String> retList = parseRecordsToStrings(ret);
str = str + "\n\t" + "消費記錄" + retList.size() + "條";
if (retList.size() > 0) {
str = str + ",如下:";
}
for (String string : retList) {
LogUtil.D(TAG, "消費記錄" + string);
str = str + "\n\t" + string;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (isoDep != null) {
try {
isoDep.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return str;
}
public static byte byteToHex(byte arg) {
byte hex = 0;
if (arg >= 48 && arg <= 57) {
hex = (byte) (arg - 48);
} else if (arg >= 65 && arg <= 70) {
hex = (byte) (arg - 55);
} else if (arg >= 97 && arg <= 102) {
hex = (byte) (arg - 87);
}
return hex;
}
private static byte[] getSelectCommand(byte[] aid) {
final ByteBuffer cmd_pse = ByteBuffer.allocate(aid.length + 6);
cmd_pse.put((byte) 0x00) // CLA Class
.put((byte) 0xA4) // INS Instruction
.put((byte) 0x04) // P1 Parameter 1
.put((byte) 0x00) // P2 Parameter 2
.put((byte) aid.length) // Lc
.put(aid).put((byte) 0x00); // Le
return cmd_pse.array();
}
// byteArray轉化為int
private static int byteToInt(byte[] b, int n) {
int ret = 0;
for (int i = 0; i < n; i++) {
ret = ret << 8;
ret |= b[i] & 0x00FF;
}
if (ret > 100000 || ret < -100000)
ret -= 0x80000000;
return ret;
}
/**
* 整條Records決議成ArrayList<byte[]>
*
* @param Records
* @return
*/
private static ArrayList<byte[]> parseRecords(byte[] Records) {
int max = Records.length / 23;
LogUtil.D(TAG, "消費記錄有" + max + "條");
ArrayList<byte[]> ret = new ArrayList<byte[]>();
for (int i = 0; i < max; i++) {
byte[] aRecord = new byte[23];
for (int j = 23 * i, k = 0; j < 23 * (i + 1); j++, k++) {
aRecord[k] = Records[j];
}
ret.add(aRecord);
}
for (byte[] bs : ret) {
LogUtil.D(TAG, "消費記錄有byte[]" + bs); // 有資料,決議正確,
}
return ret;
}
/**
* ArrayList<byte[]>記錄分析List<String> 一條記錄是23個位元組byte[] data,對其解碼如下
* data[0]-data[1]:index data[2]-data[4]:over,金額溢位??? data[5]-data[8]:交易金額
* ??代碼應該是(5,4) data[9]:如果等于0x06或者0x09,表示刷卡;否則是充值
* data[10]-data[15]:刷卡機或充值機編號
* data[16]-data[22]:日期String.format("%02X%02X.%02X.%02X %02X:%02X:%02X"
* ,data[16], data[17], data[18], data[19], data[20], data[21], data[22]);
*
* @return
*/
private static List<String> parseRecordsToStrings(ArrayList<byte[]>... Records) {
List<String> recordsList = new ArrayList<String>();
for (ArrayList<byte[]> record : Records) {
if (record == null)
continue;
for (byte[] v : record) {
StringBuilder r = new StringBuilder();
int cash = NumberUtil.toInt(v);
char t = (v[9] == TRANS_CSU || v[9] == TRANS_CSU_CPX) ? '-' : '+';
r.append(String.format("%02X%02X.%02X.%02X %02X:%02X ", v[16], v[17], v[18], v[19], v[20], v[21], v[22]));
r.append(" " + t).append(cash / 100.0f);
String aLog = r.toString();
recordsList.add(aLog);
}
}
return recordsList;
}
protected final static byte TRANS_CSU = 6; // 如果等于0x06或者0x09,表示刷卡;否則是充值
protected final static byte TRANS_CSU_CPX = 9; // 如果等于0x06或者0x09,表示刷卡;否則是充值
}
3??Manifest中的 activity注冊代碼
<activity
android:name="com.jiao.demo.nfc.NFCMainActivity"
android:configChanges="keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout|fontScale|touchscreen"
android:launchMode="singleInstance"
android:maxAspectRatio="2.4"
android:label="NFC引數修改"
android:screenOrientation="portrait">
<!-- NFC start-->
<!-- NFC 過濾目標 -->
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="vnd.android.nfc"
android:host="ext"
android:pathPrefix="/com.jiao.demo.nfc.nfcmainactivity:nfc"/>
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
</intent-filter>
<meta-data
android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/nfc_tech_filter" />
<!-- NFC start-->
</activity>
4??TECH_DISCOVERED resource nfc_tech_filter
下面是nfc_tech_filter 中的代碼, 這個檔案的位置為src/main/res/xml/nfc_tech_filter.xml
因為我測驗的Nfc 只有三個型別,所以我只寫這幾個,這也是一層對Nfc的過濾
<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>-->
<tech-list>
<tech>android.nfc.tech.MifareUltralight</tech>
<tech>android.nfc.tech.NfcA</tech>
<tech>android.nfc.tech.Ndef</tech>
</tech-list>
</resources>
5??工具類
public class NumberUtil {
public static int toInt(byte[] bytes){
int number = 0;
for(int i = 0; i < 4 ; i++){
number += bytes[i] << i*8;
}
return number;
}
}
public class StringUtil {
// 字符序列轉換為16進制字串
public static String bytesToHexString(byte[] src) {
return bytesToHexString(src, true);
}
public static String bytesToHexString(byte[] src, boolean isPrefix) {
StringBuilder stringBuilder = new StringBuilder();
if (isPrefix) {
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();
}
}
上面為 Nfc 的所有型別的讀取 以及NDEF型別的寫入,沒有其他型別的寫入,暫時沒有加密,后期會考慮新寫一篇來介紹,
二、注意事項
1、Android 如果想要啟動一個應用,并且到達指定界面,需要注意NdefRecord添加的先后順序,因為他會依次執行,type 必須是小寫,否則可能過濾失效,跳往啟動頁
2、IOS 不能主動識別鏈接之外的NdefRecord(除非主動去獲取),所以NdefRecord[]需要添加一個鏈接, iOS跳往外鏈去識別(添加到最后)
//創建訊息(錄入NFC) public static NdefRecord[] createNdefRecordByNFCUri(String content) { //這一段是對應的想要打開的Activity中添加的意圖過濾引數pathPrefix 為自定義(前面要有斜杠),其余為固定寫法 //<intent-filter> // <action android:name="android.nfc.action.NDEF_DISCOVERED" /> // <category android:name="android.intent.category.DEFAULT" /> // <data android:scheme="vnd.android.nfc" // android:host="ext" // android:pathPrefix="/com.jiao.demo.nfc.nfcmainactivity:nfc"/> //</intent-filter> // return new NdefRecord[]{ //打開App , ,第一個引數跟第二個引數就是Manifest中的android:pathPrefix,第三個引數是自己添加的引數 NdefRecord.createExternal("com.jiao.demo.nfc.nfcmainactivity", "nfc", content.getBytes()), //想要打開的應用包名(如果是組件化專案,包名必須是主專案的包名,也就是說,模塊的build.gradle 中頂部代碼為 apply plugin: 'com.android.application') //如果沒有安裝這個應用,則會跳往應用市場 NdefRecord.createApplicationRecord("com.*.*"), //打開的鏈接,這里主要用于IOS 識別 NdefRecord.createUri("https://www.baidu.com" + "?" + content), }; }
3、如果想要給 NdefRecord添加Id 只能自己去New NdefRecord但是又想達成人家代碼的效果,那就復制一下原始碼,給加個ID 比如


至此,待補充,,,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/224294.html
標籤:其他
上一篇:Android WMS筆記(1)
