Android 4.4更新了NFC的读卡器模式,具体API差异可以查看官方报告,本篇将梳理Android 4.3、Android 4.4以及之后版本的NFC读卡器模式的写法。(主要是对CPU卡的操作)
官方更新说明
新的 NFC 读取器模式允许 Activity 将所有 NFC Activity 限制为在前台时仅读取 Activity 感兴趣的标记类型。您可以使用 enableReaderMode() 为您的 Activity 启用读取器模式,提供一个 NfcAdapter.ReaderCallback 的实现,用于在检测到新的标记时接收回调。
Android 4.3 NFC读卡器模式
import android.app.PendingIntent;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.nfc.tech.NfcF;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import java.util.Arrays;
/**
* Created by ZP on 2017/9/20.
*/
public class NfcJellyBeanActivity extends AppCompatActivity {
private static final String TAG = "NfcJellyBeanActivity";
private NfcAdapter mNfcAdapter;
private PendingIntent pendingIntent;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
pendingIntent = PendingIntent.getActivity(
this, 0, new Intent(this,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
}
@Override
protected void onResume() {
Log.d(TAG, "onResume: ");
super.onResume();
String[][] techListsArray = new String[][]{
new String[]{NfcF.class.getName()},
new String[]{IsoDep.class.getName()}};
mNfcAdapter.enableForegroundDispatch(this, pendingIntent, null, techListsArray);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent != null) {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
Log.d(TAG, "onNewIntent: " + Arrays.toString(tag.getTechList()));
} else {
Log.d(TAG, "onNewIntent: ");
}
}
@Override
protected void onPause() {
Log.d(TAG, "onPause: ");
super.onPause();
mNfcAdapter.disableForegroundDispatch(this);
}
}
在生命周期onResume和onPause分别启动和禁止读卡器模式,一旦CPU卡片贴到手机NFC感应区,将回调到onNewIntent,并且NFC读取到的Tag将从intent中获取。
卡片贴紧NFC感应处后,输出日志:
D/NfcJellyBeanActivity: onNewIntent: [android.nfc.tech.IsoDep, android.nfc.tech.NfcA]
关键在于调用了NfcAdapter.enableForegroundDispatch()
方法,此方法有四个参数,官方注释对四个参数都进行了详细的说明。
第三个参数:涉及NFC标签的分发机制,官方说明如下图
第四个参数:当分发
ACTION_TECH_DISCOVERED
时,可以设置处理Tag的类型。如果两个参数都设置为null,则全部Tag都分发给当前Activity进行处理。
Android 4.4 NFC读卡器模式
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import com.ppy.nfclib.Util;
import java.util.Arrays;
/**
* Created by ZP on 2017/9/20.
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public class NfcKikKatActivity extends AppCompatActivity {
private static final String TAG = "NfcKikKatActivity";
private static final int READER_FLAGS = NfcAdapter.FLAG_READER_NFC_A
| NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK | NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS;
private NfcAdapter mNfcAdapter;
private NfcAdapter.ReaderCallback mReaderCallback = new NfcAdapter.ReaderCallback() {
@Override
public void onTagDiscovered(Tag tag) {
Log.d(TAG, "onTagDiscovered: " + Arrays.toString(tag.getTechList()));
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onResume() {
super.onResume();
enableReaderMode();
}
@Override
protected void onPause() {
super.onPause();
disableReaderMode();
}
private void enableReaderMode() {
Log.i(TAG, "Enabling reader mode");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
NfcAdapter nfc = NfcAdapter.getDefaultAdapter(this);
if (nfc != null) {
nfc.enableReaderMode(this, mReaderCallback, READER_FLAGS, null);
}
}
}
private void disableReaderMode() {
Log.i(TAG, "Disabling reader mode");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
NfcAdapter nfc = NfcAdapter.getDefaultAdapter(this);
if (nfc != null) {
nfc.disableReaderMode(this);
}
}
}
}
在生命周期onResume和onPause分别启动和禁止读卡器模式,一旦CPU卡片贴到手机NFC感应区,将回调到NfcAdapter.ReaderCallback
,并且回调中返回NFC读取到的Tag。
卡片贴紧NFC感应处后,输出日志:
D/NfcKikKatActivity: onTagDiscovered: [android.nfc.tech.IsoDep, android.nfc.tech.NfcA]
Android 4.4之后关键在于调用了NfcAdapter.enableReaderMode()
方法,此方法需要四个参数,源码注释也给出详细说明。
第二个参数是回调,一旦读取到合适的Tag,将分发给当前Activity处理时,将Tag回调。
第三个参数是Flag,设置将处理Tag的类型,并且可以开启NFC读取到Tag时是否有系统默认声音。
第四个参数可以null,也可以传一个带有
NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY
字段的Bundle,这个参数用于对卡片的延迟检测。
Android 4.4 更新的API
新增了
NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY
用于Tag的回调新增了一些Flag,值得注意的是
FLAG_READER_NO_PLATFORM_SOUNDS
,可以禁止NFC标签时默认的系统声音。
具体参考官网
NFC适配库
封装了一个NFC读卡器模式的库,主要是对CPU卡的操作API,Sample已经完成读取羊城通和深圳通的卡号、余额、交易记录。详情可以看Github,欢迎提Issue和PR,一起完善。
https://github.com/scauzhangpeng/NfcSample