zoukankan      html  css  js  c++  java
  • Android之NFC

    来源:http://blog.csdn.net/bear_huangzhen/article/details/46333421

    NFC简介:

    Near Field Communication 近场通信,是一种数据传输技术。

    与wifi、蓝牙、红外线等数据传输技术的一个主要差异就是有效距离一般不能超过4cm。

    NFC支持3种工作模式:

    1.读卡器模式;

    2.仿真卡模式;

    3.点对点模式;

    1.读卡器模式:

    通过NFC设备(支持NFC的Android手机)从带有NFC芯片的标签、贴纸、报纸、明信片等媒介读取信息,或将数据写到这些媒介中。

    2.仿真卡模式:

    是将支持NFC的手机或其他电子设备当成借记卡、信用卡、公交卡、门禁卡等IC卡使用;基本原理是将相应的IC卡中的信息(支付凭证)封装成数据包存储在支持NFC的手机中,在使用时还需要一个NFC射频器(相当于刷传统IC卡时使用的刷卡器),将手机靠近NFC射频器,手机就会收到NFC射频器发过来的信号,在通过一系列复杂的验证后,将IC卡的相应信息传入NFC射频器,最后这些IC卡数据会传入NFC射频器连接的计算机,并进行相应的处理(如电子转账、开门等操作)。

    3.点对点模式:

    与蓝牙、红外差不多,可以用于不同的NFC设备之间进行数据交换,只是NFC的点对点模式有效距离更短,不能超过4cm;但是如果两个设备使用的都是Android4.2及以上版本,NFC会直接利用蓝牙传输,这种技术被称为android Beam,所以Android Beam传输数据的两部设备不局限于4cm之内。

    基础知识:

    1.Android SDK API主要支持NFC论坛标准(Forum Standard),这种标准被称为NDEF(NFC Data Exchange Format,NFC数据交换格式);

    2.Android SDK API支持如下三种NDEF数据的操作:

    a.从NFC标签读取NDEF格式的数据;

    b.向NFC标签写入NDEF格式的数据;

    c.通过Android Beam技术将NDEF数据发送到另一部NFC设备;

    3.在一个NFC设备读取NFC标签或另一个NFC设备中的数据之前会在0.1秒的时间之内建立NFC连接,然后数据会自动从被读取一端流向读取数据的一端;数据接收端会根据具体的数据格式和标签类型调用相应的Activity(这种行为也称为Tag Dispatch),这些Activity都需要定义Intent Filter,这些Intent Filter中就会指定不同的过滤机制,分为三个级别,也称为NFC的三重过滤机制。

    4.NDEF_DISCOVERED:

    只过滤固定格式的NDEF数据。例如:纯文本、指定协议(http、ftp、smb等)的URI等;

      TECH_DISCOVERED:

    当ACTION_NDEF_DISCOVERED指定的过滤机制无法匹配Tag时,就会使用这种过滤机制进行匹配,这种过滤机制并不是通过Tag中的数据格式进行匹配的,而是根据Tag支持的数据存储格式进行匹配,因此这种过滤机制的范围更广;

      TAG_DISCOVERED:

    如果将NFC过滤机制看成if...else if...else语句的话,那么这种过滤机制就相当于else部分,当前面两种过滤机制都匹配失败后,系统就会利用这种过滤机制来处理,这种过滤机制用来处理未识别的Tag(数据格式不对,而且Tag支持的格式也不匹配)。

    5.Android系统会依次匹配NDEF_DISCOVERED、TECH_DISCOVERED和TAG_DISCOVERED;如果通过三重过滤机制仍然无法匹配Tag,则什么都不做;通常在成功匹配Tag后,Android设备会发出比较清脆的声音,而未成功匹配Tag,就会发出比较沉闷的声音。

    此过程的处理流程如下图所示:

    6.在manifest文件中需要设置的部分有:

    设置权限:

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

    限制Android版本:

    android:minSdkVersion="14"

    限制安装的设备:

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

    设置Activity的Intent Filter,比如设置为三种过滤机制的一种:

    <intent-filter>
        <action android:name="android.nfc.action.TECH_DISCOVERED" />
    </intent-filter>

    接下来,我们来第一个例子,这个例子是属于读卡器模式,从NFC芯片中读取和写入数据。

    它的manifest文件内容如下:

    [html] view plain copy
     
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    3.     package="com.r8c.nfc_demo"  
    4.     android:versionCode="110"  
    5.     android:versionName="1.1.0" >  
    6.   
    7.     <uses-sdk  
    8.         android:minSdkVersion="15"  
    9.         android:targetSdkVersion="17" />  
    10.     <!-- NFC权限声明 -->  
    11.     <uses-permission android:name="android.permission.NFC" />  
    12.   
    13.     <uses-feature  
    14.         android:name="android.hardware.nfc"  
    15.         android:required="true" />  
    16.   
    17.     <application  
    18.         android:allowBackup="true"  
    19.         android:icon="@drawable/ic_launcher"  
    20.         android:label="@string/app_name"  
    21.         android:theme="@style/AppTheme" >  
    22.   
    23.         <activity  
    24.             android:name="com.r8c.nfc_demo.NfcDemoActivity"  
    25.             android:configChanges="orientation|keyboardHidden|screenSize"  
    26.             android:label="@string/app_name"   
    27.             android:launchMode="singleTask">  
    28.             <intent-filter>  
    29.                 <action android:name="android.intent.action.MAIN" />  
    30.   
    31.                 <category android:name="android.intent.category.LAUNCHER" />  
    32.             </intent-filter>  
    33.             <!-- TECH_DISCOVERED类型的nfc -->  
    34.             <intent-filter>  
    35.                 <action android:name="android.nfc.action.TECH_DISCOVERED" />  
    36.             </intent-filter>  
    37.             <!-- 后设资源  调用自己建立的文件夹xml中的文件 -->  
    38.             <meta-data  
    39.                 android:name="android.nfc.action.TECH_DISCOVERED"  
    40.                 android:resource="@xml/nfc_tech_filter" />  
    41.         </activity>  
    42.     </application>  
    43.   
    44. </manifest>  

    它的Activity的内容如下,包括读取、写入、删除三大功能:(其中删除功能是通过写入空值来实现的)

    [java] view plain copy
     
    1. import java.io.IOException;  
    2. import java.io.UnsupportedEncodingException;  
    3. import java.nio.charset.Charset;  
    4.   
    5. import android.media.AudioManager;  
    6. import android.media.MediaPlayer;  
    7. import android.media.RingtoneManager;  
    8. import android.net.Uri;  
    9. import android.nfc.FormatException;  
    10. import android.nfc.NdefMessage;  
    11. import android.nfc.NdefRecord;  
    12. import android.nfc.NfcAdapter;  
    13. import android.nfc.Tag;  
    14. import android.nfc.tech.MifareUltralight;  
    15. import android.nfc.tech.Ndef;  
    16. import android.nfc.tech.NfcA;  
    17. import android.os.Bundle;  
    18. import android.app.Activity;  
    19. import android.app.PendingIntent;  
    20. import android.content.Context;  
    21. import android.content.Intent;  
    22. import android.content.IntentFilter;  
    23. import android.graphics.Color;  
    24. import android.util.Log;  
    25. import android.view.Menu;  
    26. import android.view.View;  
    27. import android.view.View.OnClickListener;  
    28. import android.widget.Button;  
    29. import android.widget.TextView;  
    30. import android.widget.Toast;  
    31.   
    32. public class NfcDemoActivity extends Activity implements OnClickListener {  
    33.   
    34.     // NFC适配器  
    35.     private NfcAdapter nfcAdapter = null;  
    36.     // 传达意图  
    37.     private PendingIntent pi = null;  
    38.     // 滤掉组件无法响应和处理的Intent  
    39.     private IntentFilter tagDetected = null;  
    40.     // 文本控件  
    41.     private TextView promt = null;  
    42.     // 是否支持NFC功能的标签  
    43.     private boolean isNFC_support = false;  
    44.     // 读、写、删按钮控件  
    45.     private Button readBtn, writeBtn, deleteBtn;  
    46.   
    47.     @Override  
    48.     protected void onCreate(Bundle savedInstanceState) {  
    49.         super.onCreate(savedInstanceState);  
    50.         setContentView(R.layout.activity_nfc_demo);  
    51.         setupViews();  
    52.         initNFCData();  
    53.     }  
    54.   
    55.     @Override  
    56.     protected void onResume() {  
    57.         super.onResume();  
    58.         if (isNFC_support == false) {  
    59.             // 如果设备不支持NFC或者NFC功能没开启,就return掉  
    60.             return;  
    61.         }  
    62.         // 开始监听NFC设备是否连接  
    63.         startNFC_Listener();  
    64.   
    65.         if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(this.getIntent()  
    66.                 .getAction())) {  
    67.             // 注意这个if中的代码几乎不会进来,因为刚刚在上一行代码开启了监听NFC连接,下一行代码马上就收到了NFC连接的intent,这种几率很小  
    68.             // 处理该intent  
    69.             processIntent(this.getIntent());  
    70.         }  
    71.     }  
    72.   
    73.     @Override  
    74.     protected void onPause() {  
    75.         super.onPause();  
    76.         if (isNFC_support == true) {  
    77.             // 当前Activity如果不在手机的最前端,就停止NFC设备连接的监听  
    78.             stopNFC_Listener();  
    79.         }  
    80.     }  
    81.   
    82.     @Override  
    83.     protected void onNewIntent(Intent intent) {  
    84.         super.onNewIntent(intent);  
    85.         // 当前app正在前端界面运行,这个时候有intent发送过来,那么系统就会调用onNewIntent回调方法,将intent传送过来  
    86.         // 我们只需要在这里检验这个intent是否是NFC相关的intent,如果是,就调用处理方法  
    87.         if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) {  
    88.             processIntent(intent);  
    89.         }  
    90.     }  
    91.   
    92.     @Override  
    93.     public void onClick(View v) {  
    94.   
    95.         // 点击读按钮后  
    96.         if (v.getId() == R.id.read_btn) {  
    97.             try {  
    98.                 String content = read(tagFromIntent);  
    99.                 if (content != null && !content.equals("")) {  
    100.                     promt.setText(promt.getText() + "nfc标签内容: " + content  
    101.                             + " ");  
    102.                 } else {  
    103.                     promt.setText(promt.getText() + "nfc标签内容: " + "内容为空 ");  
    104.                 }  
    105.             } catch (IOException e) {  
    106.                 promt.setText(promt.getText() + "错误:" + e.getMessage() + " ");  
    107.                 Log.e("myonclick", "读取nfc异常", e);  
    108.             } catch (FormatException e) {  
    109.                 promt.setText(promt.getText() + "错误:" + e.getMessage() + " ");  
    110.                 Log.e("myonclick", "读取nfc异常", e);  
    111.             }  
    112.             // 点击写后写入  
    113.         } else if (v.getId() == R.id.write_btn) {  
    114.             try {  
    115.                 write(tagFromIntent);  
    116.             } catch (IOException e) {  
    117.                 promt.setText(promt.getText() + "错误:" + e.getMessage() + " ");  
    118.                 Log.e("myonclick", "写nfc异常", e);  
    119.             } catch (FormatException e) {  
    120.                 promt.setText(promt.getText() + "错误:" + e.getMessage() + " ");  
    121.                 Log.e("myonclick", "写nfc异常", e);  
    122.             }  
    123.         } else if (v.getId() == R.id.delete_btn) {  
    124.             try {  
    125.                 delete(tagFromIntent);  
    126.             } catch (IOException e) {  
    127.                 promt.setText(promt.getText() + "错误:" + e.getMessage() + " ");  
    128.                 Log.e("myonclick", "删除nfc异常", e);  
    129.             } catch (FormatException e) {  
    130.                 promt.setText(promt.getText() + "错误:" + e.getMessage() + " ");  
    131.                 Log.e("myonclick", "删除nfc异常", e);  
    132.             }  
    133.         }  
    134.     }  
    135.   
    136.     private void setupViews() {  
    137.         // 控件的绑定  
    138.         promt = (TextView) findViewById(R.id.promt);  
    139.         readBtn = (Button) findViewById(R.id.read_btn);  
    140.         writeBtn = (Button) findViewById(R.id.write_btn);  
    141.         deleteBtn = (Button) findViewById(R.id.delete_btn);  
    142.         // 给文本控件赋值初始文本  
    143.         promt.setText("等待RFID标签");  
    144.         // 监听读、写、删按钮控件  
    145.         readBtn.setOnClickListener(this);  
    146.         writeBtn.setOnClickListener(this);  
    147.         deleteBtn.setOnClickListener(this);  
    148.     }  
    149.   
    150.     private void initNFCData() {  
    151.         // 初始化设备支持NFC功能  
    152.         isNFC_support = true;  
    153.         // 得到默认nfc适配器  
    154.         nfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());  
    155.         // 提示信息定义  
    156.         String metaInfo = "";  
    157.         // 判定设备是否支持NFC或启动NFC  
    158.         if (nfcAdapter == null) {  
    159.             metaInfo = "设备不支持NFC!";  
    160.             Toast.makeText(this, metaInfo, Toast.LENGTH_SHORT).show();  
    161.             isNFC_support = false;  
    162.         }  
    163.         if (!nfcAdapter.isEnabled()) {  
    164.             metaInfo = "请在系统设置中先启用NFC功能!";  
    165.             Toast.makeText(this, metaInfo, Toast.LENGTH_SHORT).show();  
    166.             isNFC_support = false;  
    167.         }  
    168.   
    169.         if (isNFC_support == true) {  
    170.             init_NFC();  
    171.         } else {  
    172.             promt.setTextColor(Color.RED);  
    173.             promt.setText(metaInfo);  
    174.         }  
    175.     }  
    176.   
    177.     @Override  
    178.     public boolean onCreateOptionsMenu(Menu menu) {  
    179.         // Inflate the menu; this adds items to the action bar if it is present.  
    180.         getMenuInflater().inflate(R.menu.nfc_demo, menu);  
    181.         return true;  
    182.     }  
    183.   
    184.     // 字符序列转换为16进制字符串  
    185.     private String bytesToHexString(byte[] src) {  
    186.         return bytesToHexString(src, true);  
    187.     }  
    188.   
    189.     private String bytesToHexString(byte[] src, boolean isPrefix) {  
    190.         StringBuilder stringBuilder = new StringBuilder();  
    191.         if (isPrefix == true) {  
    192.             stringBuilder.append("0x");  
    193.         }  
    194.         if (src == null || src.length <= 0) {  
    195.             return null;  
    196.         }  
    197.         char[] buffer = new char[2];  
    198.         for (int i = 0; i < src.length; i++) {  
    199.             buffer[0] = Character.toUpperCase(Character.forDigit(  
    200.                     (src[i] >>> 4) & 0x0F, 16));  
    201.             buffer[1] = Character.toUpperCase(Character.forDigit(src[i] & 0x0F,  
    202.                     16));  
    203.             System.out.println(buffer);  
    204.             stringBuilder.append(buffer);  
    205.         }  
    206.         return stringBuilder.toString();  
    207.     }  
    208.   
    209.     private Tag tagFromIntent;  
    210.   
    211.     /** 
    212.      * Parses the NDEF Message from the intent and prints to the TextView 
    213.      */  
    214.     public void processIntent(Intent intent) {  
    215.         if (isNFC_support == false)  
    216.             return;  
    217.   
    218.         // 取出封装在intent中的TAG  
    219.         tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);  
    220.   
    221.         promt.setTextColor(Color.BLUE);  
    222.         String metaInfo = "";  
    223.         metaInfo += "卡片ID:" + bytesToHexString(tagFromIntent.getId()) + " ";  
    224.         Toast.makeText(this, "找到卡片", Toast.LENGTH_SHORT).show();  
    225.   
    226.         // Tech List  
    227.         String prefix = "android.nfc.tech.";  
    228.         String[] techList = tagFromIntent.getTechList();  
    229.   
    230.         //分析NFC卡的类型: Mifare Classic/UltraLight Info  
    231.         String CardType = "";  
    232.         for (int i = 0; i < techList.length; i++) {  
    233.             if (techList[i].equals(NfcA.class.getName())) {  
    234.                 // 读取TAG  
    235.                 NfcA mfc = NfcA.get(tagFromIntent);  
    236.                 try {  
    237.                     if ("".equals(CardType))  
    238.                         CardType = "MifareClassic卡片类型   不支持NDEF消息  ";  
    239.                 } catch (Exception e) {  
    240.                     e.printStackTrace();  
    241.                 }  
    242.             } else if (techList[i].equals(MifareUltralight.class.getName())) {  
    243.                 MifareUltralight mifareUlTag = MifareUltralight  
    244.                         .get(tagFromIntent);  
    245.                 String lightType = "";  
    246.                 // Type Info  
    247.                 switch (mifareUlTag.getType()) {  
    248.                 case MifareUltralight.TYPE_ULTRALIGHT:  
    249.                     lightType = "Ultralight";  
    250.                     break;  
    251.                 case MifareUltralight.TYPE_ULTRALIGHT_C:  
    252.                     lightType = "Ultralight C";  
    253.                     break;  
    254.                 }  
    255.                 CardType = lightType + "卡片类型 ";  
    256.   
    257.                 Ndef ndef = Ndef.get(tagFromIntent);  
    258.                 CardType += "最大数据尺寸:" + ndef.getMaxSize() + " ";  
    259.   
    260.             }  
    261.         }  
    262.         metaInfo += CardType;  
    263.         promt.setText(metaInfo);  
    264.     }  
    265.   
    266.     // 读取方法  
    267.     private String read(Tag tag) throws IOException, FormatException {  
    268.         if (tag != null) {  
    269.             //解析Tag获取到NDEF实例  
    270.             Ndef ndef = Ndef.get(tag);  
    271.             //打开连接  
    272.             ndef.connect();  
    273.             //获取NDEF消息  
    274.             NdefMessage message = ndef.getNdefMessage();  
    275.             //将消息转换成字节数组  
    276.             byte[] data = message.toByteArray();  
    277.             //将字节数组转换成字符串  
    278.             String str = new String(data, Charset.forName("UTF-8"));  
    279.             //关闭连接  
    280.             ndef.close();  
    281.             return str;  
    282.         } else {  
    283.             Toast.makeText(NfcDemoActivity.this, "设备与nfc卡连接断开,请重新连接...",  
    284.                     Toast.LENGTH_SHORT).show();  
    285.         }  
    286.         return null;  
    287.     }  
    288.   
    289.     // 写入方法  
    290.     private void write(Tag tag) throws IOException, FormatException {  
    291.         if (tag != null) {  
    292.             //新建NdefRecord数组,本例中数组只有一个元素  
    293.             NdefRecord[] records = { createRecord() };  
    294.             //新建一个NdefMessage实例  
    295.             NdefMessage message = new NdefMessage(records);  
    296.             // 解析TAG获取到NDEF实例  
    297.             Ndef ndef = Ndef.get(tag);  
    298.             // 打开连接  
    299.             ndef.connect();  
    300.             // 写入NDEF信息  
    301.             ndef.writeNdefMessage(message);  
    302.             // 关闭连接  
    303.             ndef.close();  
    304.             promt.setText(promt.getText() + "写入数据成功!" + " ");  
    305.         } else {  
    306.             Toast.makeText(NfcDemoActivity.this, "设备与nfc卡连接断开,请重新连接...",  
    307.                     Toast.LENGTH_SHORT).show();  
    308.         }  
    309.     }  
    310.   
    311.     // 删除方法  
    312.     private void delete(Tag tag) throws IOException, FormatException {  
    313.         if (tag != null) {  
    314.             //新建一个里面无任何信息的NdefRecord实例  
    315.             NdefRecord nullNdefRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA,  
    316.                     new byte[] {}, new byte[] {}, new byte[] {});  
    317.             NdefRecord[] records = { nullNdefRecord };  
    318.             NdefMessage message = new NdefMessage(records);  
    319.             // 解析TAG获取到NDEF实例  
    320.             Ndef ndef = Ndef.get(tag);  
    321.             // 打开连接  
    322.             ndef.connect();  
    323.             // 写入信息  
    324.             ndef.writeNdefMessage(message);  
    325.             // 关闭连接  
    326.             ndef.close();  
    327.             promt.setText(promt.getText() + "删除数据成功!" + " ");  
    328.         } else {  
    329.             Toast.makeText(NfcDemoActivity.this, "设备与nfc卡连接断开,请重新连接...",  
    330.                     Toast.LENGTH_SHORT).show();  
    331.         }  
    332.     }  
    333.       
    334.     //返回一个NdefRecord实例  
    335.     private NdefRecord createRecord() throws UnsupportedEncodingException {  
    336.         //组装字符串,准备好你要写入的信息  
    337.         String msg = "BEGIN:VCARD " + "VERSION:2.1 " + "中国湖北省武汉市 "  
    338.                 + "武汉大学计算机学院 " + "END:VCARD";  
    339.         //将字符串转换成字节数组  
    340.         byte[] textBytes = msg.getBytes();  
    341.         //将字节数组封装到一个NdefRecord实例中去  
    342.         NdefRecord textRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA,  
    343.                 "text/x-vCard".getBytes(), new byte[] {}, textBytes);  
    344.         return textRecord;  
    345.     }  
    346.   
    347.     private MediaPlayer ring() throws Exception, IOException {  
    348.         // TODO Auto-generated method stub  
    349.         Uri alert = RingtoneManager  
    350.                 .getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);  
    351.         MediaPlayer player = new MediaPlayer();  
    352.         player.setDataSource(this, alert);  
    353.         final AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);  
    354.         if (audioManager.getStreamVolume(AudioManager.STREAM_NOTIFICATION) != 0) {  
    355.             player.setAudioStreamType(AudioManager.STREAM_NOTIFICATION);  
    356.             player.setLooping(false);  
    357.             player.prepare();  
    358.             player.start();  
    359.         }  
    360.         return player;  
    361.     }  
    362.   
    363.     private void startNFC_Listener() {  
    364.         // 开始监听NFC设备是否连接,如果连接就发pi意图  
    365.         nfcAdapter.enableForegroundDispatch(this, pi,  
    366.                 new IntentFilter[] { tagDetected }, null);  
    367.     }  
    368.   
    369.     private void stopNFC_Listener() {  
    370.         // 停止监听NFC设备是否连接  
    371.         nfcAdapter.disableForegroundDispatch(this);  
    372.     }  
    373.   
    374.     private void init_NFC() {  
    375.         // 初始化PendingIntent,当有NFC设备连接上的时候,就交给当前Activity处理  
    376.         pi = PendingIntent.getActivity(this, 0, new Intent(this, getClass())  
    377.                 .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);  
    378.         // 新建IntentFilter,使用的是第二种的过滤机制  
    379.         tagDetected = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);  
    380.         tagDetected.addCategory(Intent.CATEGORY_DEFAULT);  
    381.     }  
    382.   
    383. }  

    下面是该示例的完整源码下载链接:

    Android NFC Demo1

  • 相关阅读:
    H5及微信中唤起app的解决方案
    html5统计数据上报API:SendBeacon
    基于webpack4的react开发环境配置
    electron-vue开发爬坑指南
    利用git 进行多人协作开发
    js 性能优化利器:prepack
    各种渲染方式对比解析
    Nuxt.js部署应用的方式
    微信小程序--data的赋值与取值
    甘超波:什么是个人定位
  • 原文地址:https://www.cnblogs.com/zouhao/p/7153615.html
Copyright © 2011-2022 走看看