天天看點

nfc開發

    很多android裝置已經支援nfc(近距離無線通訊技術)了。本文就以執行個體的方式,為大家介紹如何在android系統中進行nfc開發。

       android nfc開發環境

       使用硬體:google nexus s,北京大學學生卡。(ps:筆者本想使用公交一卡通進行測試,發現手機不能正确識别)

       手機作業系統:android ics 4.04。

       開發時,筆者從google play store上下載下傳了nfc taginfo軟體進行對比學習。是以我們可以使用任意一張能被taginfo軟體正确識别的卡做測試。

       在android nfc 應用中,android手機通常是作為通信中的發起者,也就是作為各種nfc卡的讀寫器。android對nfc的支援主要在 android.nfc 和android.nfc.tech 兩個包中。

       android.nfc 包中主要類如下:

       nfcmanager 可以用來管理android裝置中指出的所有nfcadapter,但由于大部分android裝置隻支援一個nfc adapter,是以一般直接調用getdefaultaapater來擷取手機中的adapter。

       nfcadapter 相當于一個nfc擴充卡,類似于電腦裝了網絡擴充卡才能上網,手機裝了nfcadapter才能發起nfc通信。

       ndef: nfc data exchange format,即nfc資料交換格式。

       ndefmessage 和ndefrecord ndef 為nfc forum 定義的資料格式。

       tag 代表一個被動式tag對象,可以代表一個标簽,卡片等。當android裝置檢測到一個tag時,會建立一個tag對象,将其放在intent對象,然後發送到相應的activity。

android.nfc.tech 中則定義了可以對tag進行的讀寫操作的類,這些類按照其使用的技術類型可以分成不同的類如:nfca, nfcb, nfcf,以及mifareclassic 等。其中mifareclassic比較常見。

       在本次執行個體中,筆者使用北京大學學生卡進行資料讀取測試,學生卡的tag類型為mifareclassic。

       nfc開發執行個體講解

xml/html代碼

<span style="font-size:16px;"><?xml version="1.0" encoding="utf-8"?>   

<manifest xmlns:android="http://schemas.android.com/apk/res/android"      

    package="org.reno"      

    android:versioncode="1"      

    android:versionname="1.0" >      

    <uses-permission android:name="android.permission.nfc" />   

    <uses-sdk android:minsdkversion="14" />   

    <uses-feature android:name="android.hardware.nfc" android:required="true" />   

    <application      

        android:icon="@drawable/ic_launcher"      

        android:label="@string/app_name" >      

        <activity      

            android:name="org.reno.beam"      

            android:label="@string/app_name"      

            android:launchmode="singletop" >      

            <intent-filter>      

                <action android:name="android.intent.action.main" />   

                <category android:name="android.intent.category.launcher" />   

            </intent-filter>      

                <action android:name="android.nfc.action.tech_discovered" />   

            <meta-data      

                android:name="android.nfc.action.tech_discovered"      

                android:resource="@xml/nfc_tech_filter" />      

        </activity>      

    </application>      

</manifest>      

</span>    

       res/xml/nfc_tech_filter.xml:

<resourcesxmlns:xliffresourcesxmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">  

    <tech-list>  

       <tech>android.nfc.tech.mifareclassic</tech>  

    </tech-list>  

</resources>  

<uses-permission android:name="android.permission.nfc"/>  

<uses-feature android:name="android.hardware.nfc" android:required="true"/>  

       表示會使用到硬體的nfc功能。并且當使用者在google play store中搜尋時,隻有帶有nfc功能的手機才能夠搜尋到本應用。

       ndef_discovered, tech_discovered, tag_discovered

       當android裝置檢測到有nfc tag靠近時,會根據action申明的順序給對應的activity 發送含nfc消息的 intent。

       此處我們使用的intent-filter的action類型為tech_discovered進而可以處理所有類型為action_tech_discovered并且使用的技術為nfc_tech_filter.xml檔案中定義的類型的tag。

       下圖為當手機檢測到一個tag時,啟用activity的比對過程。

nfc開發

       res/layout/main.xml:

<?xml version="1.0" encoding="utf-8"?>   

<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"      

    android:layout_width="fill_parent"      

    android:layout_height="fill_parent"      

    android:orientation="vertical" >      

    <scrollview      

        android:id="@+id/scrollview"      

        android:layout_width="fill_parent"      

        android:layout_height="fill_parent"      

        android:background="@android:drawable/edit_text" >      

        <textview      

            android:id="@+id/promt"      

            android:layout_width="fill_parent"      

            android:layout_height="wrap_content"      

            android:scrollbars="vertical"      

            android:singleline="false"      

            android:text="@string/info" />      

    </scrollview>      

</linearlayout>  

       res/values/strings.xml:

<resources>      

    <string name="app_name">nfc測試</string>   

    <string name="info">掃描中。。。</string>   

</resources>    

       src/org/reno/beam.java:

java代碼

package org.reno;      

import android.app.activity;      

import android.content.intent;      

import android.nfc.nfcadapter;      

import android.nfc.tag;      

import android.nfc.tech.mifareclassic;      

import android.os.bundle;      

import android.widget.textview;      

public class beam extends activity {      

    nfcadapter nfcadapter;      

    textview promt;      

    @override      

    public void oncreate(bundle savedinstancestate) {      

        super.oncreate(savedinstancestate);      

        setcontentview(r.layout.main);      

        promt = (textview) findviewbyid(r.id.promt);      

        // 擷取預設的nfc控制器   

        nfcadapter = nfcadapter.getdefaultadapter(this);      

        if (nfcadapter == null) {      

            promt.settext("裝置不支援nfc!");      

            finish();      

            return;      

        }      

        if (!nfcadapter.isenabled()) {      

            promt.settext("請在系統設定中先啟用nfc功能!");      

    }      

    protected void onresume() {      

        super.onresume();      

        //得到是否檢測到action_tech_discovered觸發   

        if (nfcadapter.action_tech_discovered.equals(getintent().getaction())) {      

            //處理該intent      

            processintent(getintent());      

    //字元序列轉換為16進制字元串      

    private string bytestohexstring(byte[] src) {      

        stringbuilder stringbuilder = new stringbuilder("0x");      

        if (src == null || src.length <= 0) {      

            return null;      

        char[] buffer = new char[2];      

        for (int i = 0; i < src.length; i++) {      

            buffer[0] = character.fordigit((src[i] >>> 4) & 0x0f, 16);      

            buffer[1] = character.fordigit(src[i] & 0x0f, 16);      

            system.out.println(buffer);      

            stringbuilder.append(buffer);      

        return stringbuilder.tostring();      

    /**    

     * parses the ndef message from the intent and prints to the textview  

     */      

    private void processintent(intent intent) {      

        //取出封裝在intent中的tag      

        tag tagfromintent = intent.getparcelableextra(nfcadapter.extra_tag);      

        for (string tech : tagfromintent.gettechlist()) {      

            system.out.println(tech);      

        boolean auth = false;      

        //讀取tag      

        mifareclassic mfc = mifareclassic.get(tagfromintent);      

        try {      

            string metainfo = "";      

            //enable i/o operations to the tag from this tagtechnology object.   

            mfc.connect();      

            int type = mfc.gettype();//擷取tag的類型   

            int sectorcount = mfc.getsectorcount();//擷取tag中包含的扇區數   

            string types = "";      

            switch (type) {      

            case mifareclassic.type_classic:      

                types = "type_classic";      

                break;      

            case mifareclassic.type_plus:      

                types = "type_plus";      

            case mifareclassic.type_pro:      

                types = "type_pro";      

            case mifareclassic.type_unknown:      

                types = "type_unknown";      

            }      

            metainfo += "卡片類型:" + types + "\n共" + sectorcount + "個扇區\n共"      

                    + mfc.getblockcount() + "個塊\n存儲空間: " + mfc.getsize() + "b\n";      

            for (int j = 0; j < sectorcount; j++) {      

                //authenticate a sector with key a.   

                auth = mfc.authenticatesectorwithkeya(j,      

                        mifareclassic.key_default);      

                int bcount;      

                int bindex;      

                if (auth) {      

                    metainfo += "sector " + j + ":驗證成功\n";      

                    // 讀取扇區中的塊   

                    bcount = mfc.getblockcountinsector(j);      

                    bindex = mfc.sectortoblock(j);      

                    for (int i = 0; i < bcount; i++) {      

                        byte[] data = mfc.readblock(bindex);      

                        metainfo += "block " + bindex + " : "      

                                + bytestohexstring(data) + "\n";      

                        bindex++;      

                    }      

                } else {      

                    metainfo += "sector " + j + ":驗證失敗\n";      

                }      

            promt.settext(metainfo);      

        } catch (exception e) {      

            e.printstacktrace();      

}    

       關于mifareclassic卡的背景介紹:資料分為16個區(sector) ,每個區有4個塊(block) ,每個塊可以存放16位元組的資料。

       每個區最後一個塊稱為trailer ,主要用來存放讀寫該區block資料的key ,可以有a,b兩個key,每個key 長度為6個位元組,預設的key值一般為全ff或是0。由mifareclassic.key_default 定義。

       是以讀寫mifare tag 首先需要有正确的key值(起到保護的作用),如果鑒權成功,然後才可以讀寫該區資料。

       執行效果:

nfc開發
nfc開發
nfc開發

繼續閱讀