天天看点

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开发

继续阅读