NFC-标签内容的读取

NFC即近场通信(Near Field Communication)的英文缩写.

读取NFC标签

当开发NFC的相关应用程序时,首先我们需要在AndroidManifest.xml清单文件中,配置相关内容。
1、硬件要求:
<uses-feature
    android:name="android.hardware.nfc"
    android:required="true" />
2、权限要求:
<uses-permission android:name="android.permission.NFC" />
3.sdk版本要求:
<uses-sdk android:minSdkVersion="14"/>
4.NFC内容识别原理
当一个Android设备用于扫描一个NFC标签时,其系统将使用自己的标签分派系统解码传入的有效荷载。这个标签分派系统会分析标签,将数据分类,并使用Intent启动一个应用程序接受数据。
为使应用程序接受数据NFC数据,需要踢哪加一个Activity IntentFilter来监听某个Intent动作:
a.NfcAdapter.ACTION_NDEF_DISCOVERED  优先级最高、也是最具体的NFC消息动作。使用这个动作的Intent包括MimeType和/或URI数据。最好的做法是只要有可能,就监听这个广播,因为其extra数据允许更加具体地定义要响应的标签。
b.NfcAdapter.ACTION_TECH_DISCOVERED  当NFC技术已知、但是标签不包含数据(或者包含数据不能被映射为MineType或者URI)时广播这个动作。
c.NfcAdapter.ACTION_DISCOVERED  如果从未知技术收到一个标签,则使用此Intent动作广播该标签。

NFC两种识别过程:
第一种:使用前台分派系统
默认情况下,标签分派系统会根据标准的Intent解析过程确定哪个应用程序应该收到特定的标签,在Intent解析过程中,位于前台的Activity并不必其他应用程序优先级更高,因此,如果几个应用程序都被注册为接受扫描的标签,用户就需要选择使用哪个应用程序,即使此时你的应用程序位于前台。
通过使用前台分派系统,可以指定特定的一个具有高优先级的Activity使得当它位于前台时成为默认接受标签的应用程序,使用NFCAdapter的enable/disableForegroundDispatch方法可以切换前台分派系统。只有当一个Activity位于前台时才能使用前台分派系统,所以应该在onResume和onPause处理程序内启用和禁用改系统。

以下代码在onCreate方法中实现。
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
Intent nfcIntent = new Intent(this, getClass());
nfcIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
nfcPendingIntent = PendingIntent.getActivity(this, requestCode, nfcIntent, flags);
IntentFilter ndefIntentFilter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
ndefIntentFilter.addCategory(Intent.CATEGORY_DEFAULT);
try {
    ndefIntentFilter.addDataType("text/plain");
} catch (IntentFilter.MalformedMimeTypeException e) {
    e.printStackTrace();
}
IntentFilter tagIntentFilter = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
tagIntentFilter.addCategory(Intent.CATEGORY_DEFAULT);
IntentFilter techIntentFilter = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
techIntentFilter.addCategory(Intent.CATEGORY_DEFAULT);
IntentFilterArray = new IntentFilter[]{ndefIntentFilter, tagIntentFilter, techIntentFilter};
techListArray = new String[][]{
    new String[]{
        NfcF.class.getName()
    }
};

protected void onResume() {
    super.onResume();
    nfcAdapter.enableForegroundDispatch(
            this,
            nfcPendingIntent,    //用于打包Tag Intent的Intent
            IntentFilterArray,      //用于声明想要拦截的Intent的Intent Filter数组
            techListArray           // 想要处理的标签技术
    );
}

使用前台分派系统,返回的Intent是通过onNewIntent方法中获取到的。
protected void onNewIntent(Intent intent){
    String action = intent.getAction();
    if(TextUtils.isEmpty(action)){
        return;
    }
    if(NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)){
        parseNdef(intent);  //方法实现在后面
    }else if(NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)){
        parseTech(intent);//方法实现在后面
    }else if(NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)){
       parseTag(intent); //方法实现在后面
    }
}
第二种:一般识别过程
一般识别过程就是不使用前台分排系统,这种情况和前台分派系统不一样,不需要进入到响应的Activity的前台(也就是当前Activity的可视化页面),但是她需要在响应的Activity对应的清单文件内容中添加IntentFilter。
<activity
        android:name=".ShowActivity"
        android:launchMode="singleTop"
        android:screenOrientation="portrait">
        <intent-filter>
            <action android:name="android.nfc.action.TAG_DISCOVERED" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.nfc.action.TECH_DISCOVERED" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.nfc.action.NDEF_DISCOVERED" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="text/plain" />
        </intent-filter>
</activity>
一般情况下该Activity的启动模式为SingleTop或者SingleTask,是为了方便接受标签返回的Intent。而Intent的返回一般则该Activity生命周期方法onResume()中获得。
protected void onResume(){
    super.onReume();
    Intent intent = getIntent();
    String action = intent.getAction();
    if(TextUtils.isEmpty(action)){
        return;
    }
    if(NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)){
        parseNdef(intent);  //方法实现在后面
    }else if(NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)){
        parseTech(intent);//方法实现在后面
    }else if(NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)){
       parseTag(intent); //方法实现在后面
    }
}

以上大概已经阐述清楚了NFC标签的识别过程,下面就阐述一下,如何具体识别NFC标签中包含的数据内容。

1、MifareClassic射频卡
一般来说,给予MifareClassic的射频卡,一般内存大小有3种:
1K: 16个分区(sector),每个分区4个块(block),每个块(block) 16个byte数据。
2K: 32个分区,每个分区4个块(block),每个块(block) 16个byte数据。
4K:64个分区,每个分区4个块(block),每个块(block) 16个byte数据。

2、NFC射频卡结构
这里写图片描述
读取NFC标签内容需要知道标签的结构,当然以上图片显示的是1kb大小的NFC射频卡。
要想获得想要的内容,需要获得数据区的数据,由图可知每个块(block)的大小为16字节(byte),当然为了准确获取数据取内容,还需要根据写标签的具体结构操作。

解析NFC方法代码(以上作注释的方法)

private String parseTag(Intent intent) {
    int sectorCount = 0;
    byte[] content = {};
    Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    MifareClassic mifageClassic = MifareClassic.get(tag);
    try {
        mifageClassic.connect();
        sectorCount = mifageClassic.getSectorCount();
        int byteIndex = 0;
        for (int i = 1; i < sectorCount; i++) {
            boolean auth = mifageClassic.authenticateSectorWithKeyA(i,MifareClassic.KEY_DEFAULT);
            if (!auth) {
                return null;
            }
            int blockCount = mifageClassic.getBlockCountInSector(i);
            int blockIndex = mifageClassic.sectorToBlock(i);
            for (int j = 0; j < blockCount; j++) {
                if (j + 1 == blockCount) {
                        continue;
                }
                byte buffer[] = new byte[16];
                buffer = mifageClassic.readBlock(blockIndex);
                if (blockIndex == 4) {
                    System.arraycopy(buffer, 9, content, byteIndex, 6);
                    byteIndex += 6;
                    } else {
                      System.arraycopy(buffer, 0, content, byteIndex, 16);
                        byteIndex += 16;
                    }
                    blockIndex++;
                    Log.i("content_buffer" + blockIndex, new String(content));
                }
            }
            String contentStr = new String(content);
            return contentStr;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    private String parseNdef(Intent intent) {
        wait2Scan(true);
        Parcelable[] messages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
        NdefMessage ndefs[];
        String data = "";
        if (messages != null) {
            ndefs = new NdefMessage[messages.length];
            for (int i = 0; i < messages.length; i++) {
                ndefs[i] = (NdefMessage) messages[i];
            }
            if (ndefs != null && ndefs.length > 0) {
                NdefRecord record = ndefs[0].getRecords()[0];
                byte[] payload = record.getPayload();
                String textEncoding = ((payload[0] & 0200) == 0) ? "UTF-8" : "UTF-16";
                int languageCodeLength = payload[0] & 0077;
                try {
                    data = new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
                    textContentTips.setText(data);
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            }
        }
        return data;
    }

    private String parseTech(Intent intent) {
        return parseTag(intent);
    }

猜你喜欢

转载自blog.csdn.net/qq_35920289/article/details/78850267
NFC