NFC-标签内容的读取

来源:互联网 发布:php默认登录页面 编辑:程序博客网 时间:2024/06/05 22:36

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);    }