Android NFC读MifareClassic卡获取卡片ID
2019-02-19 本文已影响13人
程思扬
1.首先要在AndroidManifest.xml中声明如下配置信息:
为了能够使用Android手机的NFC功能,需要在Manifest文件中添加相应的权限:
详细配置请参考-->Android NFC标签读写配置过滤器总结
<uses-permission android:name="android.permission.NFC" />
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />
2.NFC TAG的发布系统:
当android设备扫描到一个NFC标签时,会自动寻找最适合的Activity来处理这个TAG,如果有多个Activity满足条件的话,会让用户来选择到底使用哪一个Activity来处理,可以理解为就是简单的事件响应与事件处理。
那么如何让一个Activity监听 ”当扫描到NFC标签时” 的这一个事件呢?使用intent filter。
可以理解为当检测到一个NFC标签时,系统自动创建一个相关Intent对象,含有响应intent-filter的Activity将处理这个Intent。
其中,intent filter声明如下:(在AndroidManifest.xml中声明在你需要捕获这个Intent的Activity里)(如下是识别公交卡的TECH格式过滤标签)即ACTION_TECH_DISCOVERED类型的过滤器:
<activity android:name=".NFCActivity" android:launchMode="singleInstance">
<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" />
</activity>
配置android:name=".NFCActivity"是为了当退出app时只要扫描卡片能直接打开app并定位到NFC扫描界面。
在res文件夹下新建一个xml的文件夹,里面放的是Android支持的NFC类型的配置数据。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>
另外还有ACTION_NDEF_DISCOVERED类型的过滤器
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
ACTION_TAG_DISCOVERED类型的过滤器
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
3.详细代码如下:
res/layout/nfc_info.xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/app_background"
>
<LinearLayout
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<AbsoluteLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:clickable="true"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:textSize="@dimen/enfore_title"
android:textColor="@color/enfore_title"
android:layout_x="0dp"
android:layout_y="0dp"
android:text="NFC测试"
android:gravity="center"
/>
<com.golden.test.iconfont.IconFontTextview
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/enfore_title"
android:textColor="@drawable/back_btn"
android:paddingTop="15dp"
android:paddingLeft="10dp"
android:text=""
android:clickable="true"
android:gravity="left|center_vertical"
android:layout_x="0dp"
android:layout_y="0dp"
android:onClick="btn_back"
/>
</AbsoluteLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="5dp"
>
<TextView
android:id="@+id/promt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/enfore_title"
android:text="NFC扫描中..."
android:textSize="@dimen/app_info"
/>
</LinearLayout>
</LinearLayout>
</ScrollView>
NFCActivity.java
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.MifareClassic;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
public class NFCActivity extends Activity {
NfcAdapter nfcAdapter;
TextView promt;
private PendingIntent pi;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.nfc_info);
promt = (TextView) findViewById(R.id.promt);
// 获取默认的NFC控制器
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
pi = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
if (nfcAdapter == null) {
Toast.makeText(this, "对不起,您的设备不支持nfc功能!", Toast.LENGTH_SHORT).show();
//promt.setText("设备不支持NFC!");
finish();
return;
}
if (!nfcAdapter.isEnabled()) {
Toast.makeText(this, "请在系统设置中开启NFC功能!", Toast.LENGTH_SHORT).show();
//promt.setText("请在系统设置中先启用NFC功能!");
finish();
return;
}
}
public void btn_back(View view){
this.finish();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// 当前app正在前端界面运行,这个时候有intent发送过来,那么系统就会调用onNewIntent回调方法,将intent传送过来
// 我们只需要在这里检验这个intent是否是NFC相关的intent,如果是,就调用处理方法
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
processIntent(intent);
}
}
//页面获取焦点
@Override
protected void onResume() {
super.onResume();
nfcAdapter.enableForegroundDispatch(this, pi, null, null);
}
//页面失去焦点
@Override
protected void onPause() {
super.onPause();
if(nfcAdapter!=null){
nfcAdapter.disableForegroundDispatch(this);//关闭前台发布系统
}
}
/*@Override
protected void onResume() {
super.onResume();
//得到是否检测到ACTION_TECH_DISCOVERED触发
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(getIntent().getAction())) {
//处理该intent
processIntent(getIntent());
}
}*/
//字符序列转换为16进制字符串
private String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("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.forDigit((src[i] >>> 4) & 0x0F, 16);
buffer[1] = Character.forDigit(src[i] & 0x0F, 16);
System.out.println(buffer);
stringBuilder.append(buffer);
}
return stringBuilder.toString();
}
private 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;
}
/**
* Parses the NDEF Message from the intent and prints to the TextView
*/
private void processIntent(Intent intent) {
//取出封装在intent中的TAG
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
String CardId =ByteArrayToHexString(tagFromIntent.getId());
String metaInfo = "";
metaInfo+="卡片ID:"+CardId;
for (String tech : tagFromIntent.getTechList()) {
System.out.println(tech);
}
boolean auth = false;
//读取TAG
MifareClassic mfc = MifareClassic.get(tagFromIntent);
try {
//Enable I/O operations to the tag from this TagTechnology object.
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 + " : "
+ bytesToHexString(data) + "\n";
bIndex++;
}
} else {
metaInfo += "Sector " + j + ":验证失败\n";
}
}
promt.setText(metaInfo);
//Toast.makeText(this, metaInfo, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.运行效果图如下:
image.png