学习链接:
https://developer.android.com/guide/topics/connectivity/nfc/nfc
https://www.kancloud.cn/alex_wsc/android-wifi-nfc-gps/414141
第一步:AndroidManifest.xml中申明权限
添加了这句代码,会使得在应用商店下载应用是过滤掉不支持NFC功能的设备。
第二步:AndroidManifest.xml中申明与NFC目标交互的activity
这里声明了三种intent筛选条件,分别是action为
android.nfc.action.NDEF_DISCOVERED,
android.nfc.action.TECH_DISCOVERED,
android.nfc.action.TAG_DISCOVERED。
这一步可以帮助android系统内的NFC模块在扫描到一个NFC目标的时候寻找到我们的activity来进行交互。
android系统内的NFC模块在扫描到一个NFC目标后会通过以下几步来寻找到合适的处理NFC消息的activity。在这之前先介绍两个概念,1、Tag代表一个NFC目标,当android设备扫描到一个NFC目标的时候会将相关数据封装成一个Tag实例,通过Intent传递给合适的activity去做处理。2、TagTechnology表示NFC标签支持的技术,可以通过Tag的getTechList()获取。
Tag的分发步骤:
1、先看Tag中是否包含了系统支持的NDEF数据,如果包含则分发给注册了action为ACTION_NEDF_DISCOVERED的activity。
2、如果Tag中不包含系统支持的NDEF数据或者没有找到注册了action为ACTION_NDEF_DISCOVERED的activity,则NFC系统模块尝试分发给一个action为ACTION_TECH_DISCOVERED的activity。NFC系统模块在分发是首先分析NFC Tag支持的Tag Technology,然后寻找支持这类Tag Technology的activity,然后封装了Tag数据的Intent分发给对应的activity。
3、如果上面两种都不满足,则发送action为ACTION_TAG_DISCOVERED的intent
第三步,编写activity及相关工具类,和NFC目标进行读卡,发送指令等交互动作
public class MainActivity extends AppCompatActivity implements NfcView{
private static final String TAG = MainActivity.class.getName();
@BindView(R.id.rev_data) EditText mRevDataEt;
@BindView(R.id.send_command_et) EditText mSendCommandEt;
@BindView(R.id.read_btn) Button mReadCardBtn;
@BindView(R.id.send_command_btn) Button mSendCommandBtn;
private NfcHandler mNfcHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate()! ");
mNfcHandler = new NfcHandler(this);
mNfcHandler.init(this);
ButterKnife.bind(this);
}
@Override
protected void onNewIntent(Intent intent) {
Log.d(TAG, "onNewIntent()! action is:" + intent.getAction());
super.onNewIntent(intent);
setIntent(intent);
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume()! intent action is:" + getIntent().getAction());
mNfcHandler.enableNfc(this);
}
@Override
protected void onPause() {
super.onPause();
mNfcHandler.disableNfc(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
mNfcHandler.onDestroy();
}
@Override
public void appendResponse(String response) {
mRevDataEt.append(response);
}
@OnClick({R.id.send_command_btn, R.id.read_btn})
void onBtnClick(View view) {
switch (view.getId()) {
case R.id.read_btn:
mNfcHandler.readCardId(getIntent());
break;
case R.id.send_command_btn:
mNfcHandler.sendCommand(this, mSendCommandEt.getText().toString());
break;
default:
break;
}
}
}
public class NfcHandler {
private NfcAdapter mNfcAdapter;
private IsoDep mIsoDep;
private NfcView mView;
public NfcHandler(NfcView view) {
this.mView = view;
}
public void init(Context context) {
mNfcAdapter = NfcAdapter.getDefaultAdapter(context);
}
private boolean checkNfc(Context context) {
if (mNfcAdapter == null) {
Toast.makeText(context, "未找到NFC设备!", Toast.LENGTH_SHORT).show();
return false;
} else if (!mNfcAdapter.isEnabled()) {
Toast.makeText(context, "请在设置中打开NFC开关!", Toast.LENGTH_SHORT).show();
return false;
}
return true;
}
/**
* 通过tag.getTechList()可以获取当前目标Tag支持的Tag Technology,这里默认支持IsoDep。
* 通过IsoDep.get(tag)方式获取IsoDep的实例。然后通过函数connect()我们应用和IC卡之间建立联系,
* 建立联系后我们可以往IC卡发送指令进行交互。
* @param intent
*/
private void connectNfc(Intent intent) {
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction()) ||
NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction()) ||
NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (tag != null) {
mIsoDep = IsoDep.get(tag);
try {
mIsoDep.connect(); //这里建立我们应用和IC卡
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 这个函数可以获取IC卡的序列号
* @param intent
*/
public void readCardId(Intent intent) {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (tag != null && mView != null) {
byte[] ids = tag.getId();
String uid = DataUtil.bytesToHexString(ids, ids.length);
mView.appendResponse("\n uid is:" + uid);
}
}
public void sendCommand(final Context context, final String command) {
Observable observable = Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter emitter){
if (mIsoDep == null) {
emitter.onError(new Throwable("NFC设备未连接!"));
return;
}
try {
if (!mIsoDep.isConnected()) {
mIsoDep.connect();
}
byte[] sendData = DataUtil.hexStringToBytes(command);
if (sendData == null) {
emitter.onError(new Throwable("指令输入有误!"));
return;
}
byte[] responseData = mIsoDep.transceive(sendData);
emitter.onNext(DataUtil.bytesToHexString(responseData, responseData.length));
} catch (IOException e) {
emitter.onError(new Throwable("NFC连接中断!"));
}
}
});
observable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer() {
@Override
public void accept(String s) {
if (s == null) {
Toast.makeText(context, "指令输入有误!", Toast.LENGTH_SHORT).show();
} else {
mView.appendResponse("\n apdu is:" + s);
}
}
}, new Consumer() {
@Override
public void accept(Throwable throwable) {
Toast.makeText(context, throwable.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
public void enableNfc(Activity activity) {
if (checkNfc(activity)) {
PendingIntent pendingIntent = PendingIntent.getActivity(activity,
0, new Intent(activity, activity.getClass()), 0);
mNfcAdapter.enableForegroundDispatch(activity, pendingIntent, null, null);
connectNfc(activity.getIntent());
}
}
public void disableNfc(Activity activity) {
if (mIsoDep != null && mIsoDep.isConnected()) {
try{
mIsoDep.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (mNfcAdapter != null) {
mNfcAdapter.disableForegroundDispatch(activity);
}
}
public void onDestroy() {
mView = null;
}
}
Demo地址:https://github.com/Wcuren/android_nfc_demo