Android适配 - 实现4.3以后NFC的CardReader模式

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标签的分发机制,官方说明如下图
    Android适配 - 实现4.3以后NFC的CardReader模式_第1张图片

  • 第四个参数:当分发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

你可能感兴趣的:(Android)