• 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开发实例解说

            AndroidManifest.xml

    XML/HTML代码
    1. <span style="font-size:16px;"><?xml version="1.0" encoding="utf-8"?>      
    2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"      
    3.     package="org.reno"      
    4.     android:versionCode="1"      
    5.     android:versionName="1.0" >      
    6.     <uses-permission android:name="android.permission.NFC" />      
    7.     <uses-sdk android:minSdkVersion="14" />      
    8.     <uses-feature android:name="android.hardware.nfc" android:required="true" />      
    9.     <application      
    10.         android:icon="@drawable/ic_launcher"      
    11.         android:label="@string/app_name" >      
    12.         <activity      
    13.             android:name="org.reno.Beam"      
    14.             android:label="@string/app_name"      
    15.             android:launchMode="singleTop" >      
    16.             <intent-filter>      
    17.                 <action android:name="android.intent.action.MAIN" />      
    18.       
    19.                 <category android:name="android.intent.category.LAUNCHER" />      
    20.             </intent-filter>      
    21.             <intent-filter>      
    22.                 <action android:name="android.nfc.action.TECH_DISCOVERED" />      
    23.             </intent-filter>      
    24.             <meta-data      
    25.                 android:name="android.nfc.action.TECH_DISCOVERED"      
    26.                 android:resource="@xml/nfc_tech_filter" />      
    27.         </activity>      
    28.     </application>      
    29. </manifest>      
    30. </span>    

           res/xml/nfc_tech_filter.xml:

    XML/HTML代码
    1. <resourcesxmlns:xliffresourcesxmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">  
    2.     <tech-list>  
    3.        <tech>android.nfc.tech.MifareClassic</tech>  
    4.     </tech-list>  
    5. </resources>  
    6.   
    7. <uses-permission android:name="android.permission.NFC"/>  
    8. <uses-feature android:name="android.hardware.nfc" android:required="true"/>  

           表示会使用到硬件的NFC功能。而且当用户在Google Play Store中搜索时。仅仅有带有NFC功能的手机才可以搜索到本应用。

           当手机开启了NFC。而且检測到一个TAG后,TAG分发系统会自己主动创建一个封装了NFC TAG信息的intent。假设多于一个应用程序可以处理这个intent的话,那么手机就会弹出一个框。让用户选择处理该TAG的Activity。TAG分发系统定义了3中intent。

    按优先级从高到低排列为:

           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的匹配过程。

    Android系统NFC开发之实例解说

           res/layout/main.xml:

    XML/HTML代码
    1. <?xml version="1.0" encoding="utf-8"?>      
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"      
    3.     android:layout_width="fill_parent"      
    4.     android:layout_height="fill_parent"      
    5.     android:orientation="vertical" >      
    6.       
    7.     <ScrollView      
    8.         android:id="@+id/scrollView"      
    9.         android:layout_width="fill_parent"      
    10.         android:layout_height="fill_parent"      
    11.         android:background="@android:drawable/edit_text" >      
    12.       
    13.         <TextView      
    14.             android:id="@+id/promt"      
    15.             android:layout_width="fill_parent"      
    16.             android:layout_height="wrap_content"      
    17.             android:scrollbars="vertical"      
    18.             android:singleLine="false"      
    19.             android:text="@string/info" />      
    20.     </ScrollView>      
    21.       
    22. </LinearLayout>  

           定义了Activity的布局:仅仅有一个带有滚动栏的TextView用于显示从TAG中读取的信息。

           res/values/strings.xml:

    XML/HTML代码
    1. <?xml version="1.0" encoding="utf-8"?

      >      

    2. <resources>      
    3.     <string name="app_name">NFC測试</string>      
    4.     <string name="info">扫描中。。。</string>      
    5. </resources>    

           src/org/reno/Beam.java:

    Java代码
    1. package org.reno;      
    2.       
    3. import android.app.Activity;      
    4. import android.content.Intent;      
    5. import android.nfc.NfcAdapter;      
    6. import android.nfc.Tag;      
    7. import android.nfc.tech.MifareClassic;      
    8. import android.os.Bundle;      
    9. import android.widget.TextView;      
    10.       
    11. public class Beam extends Activity {      
    12.     NfcAdapter nfcAdapter;      
    13.     TextView promt;      
    14.     @Override      
    15.     public void onCreate(Bundle savedInstanceState) {      
    16.         super.onCreate(savedInstanceState);      
    17.         setContentView(R.layout.main);      
    18.         promt = (TextView) findViewById(R.id.promt);      
    19.         // 获取默认的NFC控制器      
    20.         nfcAdapter = NfcAdapter.getDefaultAdapter(this);      
    21.         if (nfcAdapter == null) {      
    22.             promt.setText("设备不支持NFC。");      
    23.             finish();      
    24.             return;      
    25.         }      
    26.         if (!nfcAdapter.isEnabled()) {      
    27.             promt.setText("请在系统设置中先启用NFC功能。");      
    28.             finish();      
    29.             return;      
    30.         }      
    31.     }      
    32.       
    33.     @Override      
    34.     protected void onResume() {      
    35.         super.onResume();      
    36.         //得到是否检測到ACTION_TECH_DISCOVERED触发      
    37.         if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(getIntent().getAction())) {      
    38.             //处理该intent      
    39.             processIntent(getIntent());      
    40.         }      
    41.     }      
    42.     //字符序列转换为16进制字符串      
    43.     private String bytesToHexString(byte[] src) {      
    44.         StringBuilder stringBuilder = new StringBuilder("0x");      
    45.         if (src == null || src.length <= 0) {      
    46.             return null;      
    47.         }      
    48.         char[] buffer = new char[2];      
    49.         for (int i = 0; i < src.length; i++) {      
    50.             buffer[0] = Character.forDigit((src[i] >>> 4) & 0x0F16);      
    51.             buffer[1] = Character.forDigit(src[i] & 0x0F16);      
    52.             System.out.println(buffer);      
    53.             stringBuilder.append(buffer);      
    54.         }      
    55.         return stringBuilder.toString();      
    56.     }      
    57.       
    58.     /**    
    59.      * Parses the NDEF Message from the intent and prints to the TextView    
    60.      */      
    61.     private void processIntent(Intent intent) {      
    62.         //取出封装在intent中的TAG      
    63.         Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);      
    64.         for (String tech : tagFromIntent.getTechList()) {      
    65.             System.out.println(tech);      
    66.         }      
    67.         boolean auth = false;      
    68.         //读取TAG      
    69.         MifareClassic mfc = MifareClassic.get(tagFromIntent);      
    70.         try {      
    71.             String metaInfo = "";      
    72.             //Enable I/O operations to the tag from this TagTechnology object.      
    73.             mfc.connect();      
    74.             int type = mfc.getType();//获取TAG的类型      
    75.             int sectorCount = mfc.getSectorCount();//获取TAG中包括的扇区数      
    76.             String typeS = "";      
    77.             switch (type) {      
    78.             case MifareClassic.TYPE_CLASSIC:      
    79.                 typeS = "TYPE_CLASSIC";      
    80.                 break;      
    81.             case MifareClassic.TYPE_PLUS:      
    82.                 typeS = "TYPE_PLUS";      
    83.                 break;      
    84.             case MifareClassic.TYPE_PRO:      
    85.                 typeS = "TYPE_PRO";      
    86.                 break;      
    87.             case MifareClassic.TYPE_UNKNOWN:      
    88.                 typeS = "TYPE_UNKNOWN";      
    89.                 break;      
    90.             }      
    91.             metaInfo += "卡片类型:" + typeS + " 共" + sectorCount + "个扇区 共"      
    92.                     + mfc.getBlockCount() + "个块 存储空间: " + mfc.getSize() + "B ";      
    93.             for (int j = 0; j < sectorCount; j++) {      
    94.                 //Authenticate a sector with key A.      
    95.                 auth = mfc.authenticateSectorWithKeyA(j,      
    96.                         MifareClassic.KEY_DEFAULT);      
    97.                 int bCount;      
    98.                 int bIndex;      
    99.                 if (auth) {      
    100.                     metaInfo += "Sector " + j + ":验证成功 ";      
    101.                     // 读取扇区中的块      
    102.                     bCount = mfc.getBlockCountInSector(j);      
    103.                     bIndex = mfc.sectorToBlock(j);      
    104.                     for (int i = 0; i < bCount; i++) {      
    105.                         byte[] data = mfc.readBlock(bIndex);      
    106.                         metaInfo += "Block " + bIndex + " : "      
    107.                                 + bytesToHexString(data) + " ";      
    108.                         bIndex++;      
    109.                     }      
    110.                 } else {      
    111.                     metaInfo += "Sector " + j + ":验证失败 ";      
    112.                 }      
    113.             }      
    114.             promt.setText(metaInfo);      
    115.         } catch (Exception e) {      
    116.             e.printStackTrace();      
    117.         }      
    118.     }      
    119. }    

           关于MifareClassic卡的背景介绍:数据分为16个区(Sector) ,每一个区有4个块(Block) 。每一个块能够存放16字节的数据。

           每一个区最后一个块称为Trailer ,主要用来存放读写该区Block数据的Key ,能够有A,B两个Key,每一个Key 长度为6个字节。缺省的Key值一般为全FF或是0。由MifareClassic.KEY_DEFAULT 定义。

           因此读写Mifare Tag 首先须要有正确的Key值(起到保护的作用),假设鉴权成功,然后才干够读写该区数据。

           运行效果:

    Android系统NFC开发之实例解说

    Android系统NFC开发之实例解说

    Android系统NFC开发之实例解说

  • 相关阅读:
    《哈佛商业评论》2018正刊12期与增刊25期的点评
    《财经》2018年共30+1期的点评与摘抄
    4星|《人人都在说谎》:社会科学方面有趣的数据分析方法与结论
    3星|侯世达《我是个怪圈》:关于人类意识的各种哲学思辨
    虚拟机 SUSE Linux Enterprise Server 12 SP2 64
    虚拟机 CentOS7 64
    虚拟机 ubuntu 16.04
    虚拟机 windows xp sp3 原版
    C#实现控制Windows系统关机、重启和注销的方法
    日期时间设置 "2018-05-04T16:36:23.6341371+08:00" 格式
  • 原文地址:https://www.cnblogs.com/cxchanpin/p/6728088.html
Copyright © 2020-2023  润新知