文件配置
要实现前台分发功能,首先需要配置Manifest文件,其中LanchActivity只是一个简单的启动页面,跳转到MainActivity(这才是要处理NFC标签的页面)。按官方的文档,处理标签扫描结果的Activity就是程序的启动页面,但是我们一般不会在启动页面处理数据。
<uses-permission android:name="android.permission.NFC"/>
<uses-feature
android:name="android.hardware.nfc"
android:required="true"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<meta-data android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/nfc_tech_filter"/>
</activity>
<activity android:name=".LanchActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
@xml/nfc_tech_filter文件,该文件中是应用程序支持的NFC技术列表
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<tech-list>
<tech>android.nfc.tech.MifareClassic</tech>
</tech-list>
</resources>
官网文档支持的所有技术列表如下(我们只需要是其子集即可):
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<tech-list>
<tech>android.nfc.tech.IsoDep</tech>
<tech>android.nfc.tech.NfcA</tech>
<tech>android.nfc.tech.NfcB</tech>
<tech>android.nfc.tech.NfcF</tech>
<tech>android.nfc.tech.NfcV</tech>
<tech>android.nfc.tech.Ndef</tech>
<tech>android.nfc.tech.NdefFormatable</tech>
<tech>android.nfc.tech.MifareClassic</tech>
<tech>android.nfc.tech.MifareUltralight</tech>
</tech-list>
</resources>
根据官网说明,标签分发拦截有以下步骤:
1、在onCreate方法中创建全局的PendingIntent对象,以便扫描到NFC标签时系统可以用扫描到的tag信息填充该pendingIntent对象。代码如下:
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
2、定义你要拦截的intent过滤器。当设备扫描到一个标签时,系统前台分发系统根据收到的intent来校验是否符合我们指定的intentFilter。如果匹配,应用程序将会处理该intent,否则,调用系统intent分发系统。如果指定intent过滤器和技术过滤数组为空,你将会接收到TAG_DISCOVERED上的所有标记。(即,如果要拦截系统标签分发系统到我们前台Activity,需要定义拦截的intent过滤器、我们要处理的技术过滤数组)
系统示例代码如下:
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
ndef.addDataType("*/*"); /* Handles all MIME based dispatches.
You should specify only the ones that you need. */
}
catch (MalformedMimeTypeException e) {
throw new RuntimeException("fail", e);
}
intentFiltersArray = new IntentFilter[] {ndef, };
在我测试的过程中,没有用到mime类型:
techListsArray = new String[][]{new String[]{MifareClassic.class.getName()}};//支持的技术类别
IntentFilter ndef = new IntentFilter();
ndef.addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);//要过滤的标签级别
intentFiltersArray = new IntentFilter[]{ndef};
3、覆写系统方法
@Override
protected void onNewIntent(Intent intent) {
processIntent(intent);//处理返回数据的方法,扫描到的标签数据都在intent中
}
@Override
protected void onPause() {
super.onPause();
nfcAdapter.disableForegroundDispatch(this);
}
@Override
protected void onResume() {
super.onResume();
nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray);
if (getIntent().getAction() != null) {
if (NfcAdapter.ACTION_TECH_DISCOVERED.endsWith(getIntent().getAction())) {
processIntent(getIntent());
}
}
}
其中,onResume方法中的if判断是为了当程序未运行时扫描到NFC标签也可以接收到数据。
然后重要的是数据读写,这里只是简单的操作,具体还是要参考官方文档:
//读取所有block中的数据
private void readData(final MifareClassic mfc) {
if (mfc != null) {
if (mfc.isConnected()) {
try {
String result = "";
tvStatus.setText("正在读取数据");
for (int i = 0; i < mfc.getSectorCount(); i++) {//循环读取所有的扇区
int bindex;
int bCount;
if (mfc.authenticateSectorWithKeyA(i, MifareClassic.KEY_DEFAULT)) {//读取的时候要校验key,否则无法读取
bindex = mfc.sectorToBlock(i);
bCount = mfc.getBlockCountInSector(i);
result += "Sector " + i + "验证成功\n";
for (int j = 0; j < bCount; j++) {//循环读取指定扇区所有的块,每个扇区最后一个块是该块的key,除非要加密,否则不要轻易改变
byte[] data = mfc.readBlock(bindex);
result += "Block " + bindex + " : " + new String(data, Charset.forName("UTF-8")) + "\n";
bindex++;
}
}
}
tvStatus.setText("数据读取完成");
tvReadData.setText(result);
} catch (Exception ex) {
tvStatus.setText("读取失败:" + ex.getMessage());
}
}
} else {
Toast.makeText(this, "未扫描到NFC", Toast.LENGTH_SHORT).show();
}
}
//向标签指定block写入数据
private void writeData(final MifareClassic mfc, final int blockIndex, String msg) {
if (mfc != null) {
try {
Log.e(TAG, "writeData: isConnected:" + mfc.isConnected());
if (mfc.isConnected()) {
byte[] temp = msg.getBytes(Charset.forName("UTF-8"));
final byte[] write = new byte[MifareClassic.BLOCK_SIZE];//每一块最大存储字节数
for (int i = 0; i < MifareClassic.BLOCK_SIZE; i++) {
if (i < temp.length)
write[i] = temp[i];
else
write[i] = 0;
}
tvStatus.setText("正在写入");
new Thread(new Runnable() {
@Override
public void run() {
try {
Log.e(TAG, "run: 写入内容:" + new String(write));
mfc.writeBlock(blockIndex, write);//写入方法是一个阻塞函数,不能在UI线程调用
runOnUiThread(new Runnable() {
@Override
public void run() {
tvStatus.setText("写入完成");
}
});
} catch (final Exception ex) {
Log.e(TAG, "writeData_Ex: " + ex.getMessage());
runOnUiThread(new Runnable() {
@Override
public void run() {
tvStatus.setText("写入失败:" + ex.getMessage());
}
});
}
}
}).start();
} else {
Toast.makeText(this, "NFC连接断开", Toast.LENGTH_SHORT).show();
}
} catch (Exception ex) {
Log.e(TAG, "writeData: " + ex.getMessage());
}
} else {
Toast.makeText(this, "未扫描到NFC卡片", Toast.LENGTH_SHORT).show();
}
}
NFCWRDemo
PS:写的不好,仅为记录
网友评论