nfc近场通信

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文件内容如下:

 <?xml version="1.0" encoding="utf-8"?> 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 
  package="com.r8c.nfc_demo" 
  android:versionCode="110" 
  android:versionName="1.1.0" > 
  
  <uses-sdk 
  android:minSdkVersion="15" 
  android:targetSdkVersion="17" /> 
  <!-- NFC权限声明 --> 
  <uses-permission android:name="android.permission.NFC" /> 
  
  <uses-feature 
  android:name="android.hardware.nfc" 
  android:required="true" /> 
  
  <application 
  android:allowBackup="true" 
  android:icon="@drawable/ic_launcher" 
  android:label="@string/app_name" 
  android:theme="@style/AppTheme" > 
  
  <activity 
  android:name="com.r8c.nfc_demo.NfcDemoActivity" 
  android:configChanges="orientation|keyboardHidden|screenSize" 
  android:label="@string/app_name" 
  android:launchMode="singleTask"> 
  <intent-filter> 
  <action android:name="android.intent.action.MAIN" /> 
  
  <category android:name="android.intent.category.LAUNCHER" /> 
  </intent-filter> 
  <!-- TECH_DISCOVERED类型的nfc --> 
  <intent-filter> 
  <action android:name="android.nfc.action.TECH_DISCOVERED" /> 
  </intent-filter> 
  <!-- 后设资源  调用自己建立的文件夹xml中的文件 --> 
  <meta-data 
  android:name="android.nfc.action.TECH_DISCOVERED" 
  android:resource="@xml/nfc_tech_filter" /> 
  </activity> 
  </application> 
  
 </manifest> 

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

 import java.io.IOException;  
 import java.io.UnsupportedEncodingException;  
 import java.nio.charset.Charset;  
  
 import android.media.AudioManager;  
 import android.media.MediaPlayer;  
 import android.media.RingtoneManager;  
 import android.net.Uri;  
 import android.nfc.FormatException;  
 import android.nfc.NdefMessage;  
 import android.nfc.NdefRecord;  
 import android.nfc.NfcAdapter;  
 import android.nfc.Tag;  
 import android.nfc.tech.MifareUltralight;  
 import android.nfc.tech.Ndef;  
 import android.nfc.tech.NfcA;  
 import android.os.Bundle;  
 import android.app.Activity;  
 import android.app.PendingIntent;  
 import android.content.Context;  
 import android.content.Intent;  
 import android.content.IntentFilter;  
 import android.graphics.Color;  
 import android.util.Log;  
 import android.view.Menu;  
 import android.view.View;  
 import android.view.View.OnClickListener;  
 import android.widget.Button;  
 import android.widget.TextView;  
 import android.widget.Toast;  
  
 public class NfcDemoActivity extends Activity implements OnClickListener {  
  
  // NFC适配器 
  private NfcAdapter nfcAdapter = null;  
  // 传达意图 
  private PendingIntent pi = null;  
  // 滤掉组件无法响应和处理的Intent 
  private IntentFilter tagDetected = null;  
  // 文本控件 
  private TextView promt = null;  
  // 是否支持NFC功能的标签 
  private boolean isNFC_support = false;  
  // 读、写、删按钮控件 
  private Button readBtn, writeBtn, deleteBtn;  
  
  @Override 
  protected void onCreate(Bundle savedInstanceState) {  
  super.onCreate(savedInstanceState);  
         setContentView(R.layout.activity_nfc_demo);  
         setupViews();  
         initNFCData();  
     }  
  
  @Override 
  protected void onResume() {  
  super.onResume();  
  if (isNFC_support == false) {  
  // 如果设备不支持NFC或者NFC功能没开启,就return掉 
  return;  
         }  
  // 开始监听NFC设备是否连接 
         startNFC_Listener();  
  
  if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(this.getIntent()  
                 .getAction())) {  
  // 注意这个if中的代码几乎不会进来,因为刚刚在上一行代码开启了监听NFC连接,下一行代码马上就收到了NFC连接的intent,这种几率很小 
  // 处理该intent 
             processIntent(this.getIntent());  
         }  
     }  
  
  @Override 
  protected void onPause() {  
  super.onPause();  
  if (isNFC_support == true) {  
  // 当前Activity如果不在手机的最前端,就停止NFC设备连接的监听 
             stopNFC_Listener();  
         }  
     }  
  
  @Override 
  protected void onNewIntent(Intent intent) {  
  super.onNewIntent(intent);  
  // 当前app正在前端界面运行,这个时候有intent发送过来,那么系统就会调用onNewIntent回调方法,将intent传送过来 
  // 我们只需要在这里检验这个intent是否是NFC相关的intent,如果是,就调用处理方法 
  if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) {  
             processIntent(intent);  
         }  
     }  
  
  @Override 
  public void onClick(View v) {  
  
  // 点击读按钮后 
  if (v.getId() == R.id.read_btn) {  
  try {  
                 String content = read(tagFromIntent);  
  if (content != null && !content.equals("")) {  
                     promt.setText(promt.getText() + "nfc标签内容:\n" + content  
                             + "\n");  
                 } else {  
                     promt.setText(promt.getText() + "nfc标签内容:\n" + "内容为空\n");  
                 }  
             } catch (IOException e) {  
                 promt.setText(promt.getText() + "错误:" + e.getMessage() + "\n");  
                 Log.e("myonclick", "读取nfc异常", e);  
             } catch (FormatException e) {  
                 promt.setText(promt.getText() + "错误:" + e.getMessage() + "\n");  
                 Log.e("myonclick", "读取nfc异常", e);  
             }  
  // 点击写后写入 
         } else if (v.getId() == R.id.write_btn) {  
  try {  
                 write(tagFromIntent);  
             } catch (IOException e) {  
                 promt.setText(promt.getText() + "错误:" + e.getMessage() + "\n");  
                 Log.e("myonclick", "写nfc异常", e);  
             } catch (FormatException e) {  
                 promt.setText(promt.getText() + "错误:" + e.getMessage() + "\n");  
                 Log.e("myonclick", "写nfc异常", e);  
             }  
         } else if (v.getId() == R.id.delete_btn) {  
  try {  
                 delete(tagFromIntent);  
             } catch (IOException e) {  
                 promt.setText(promt.getText() + "错误:" + e.getMessage() + "\n");  
                 Log.e("myonclick", "删除nfc异常", e);  
             } catch (FormatException e) {  
                 promt.setText(promt.getText() + "错误:" + e.getMessage() + "\n");  
                 Log.e("myonclick", "删除nfc异常", e);  
             }  
         }  
     }  
  
  private void setupViews() {  
  // 控件的绑定 
         promt = (TextView) findViewById(R.id.promt);  
         readBtn = (Button) findViewById(R.id.read_btn);  
         writeBtn = (Button) findViewById(R.id.write_btn);  
         deleteBtn = (Button) findViewById(R.id.delete_btn);  
  // 给文本控件赋值初始文本 
         promt.setText("等待RFID标签");  
  // 监听读、写、删按钮控件 
         readBtn.setOnClickListener(this);  
         writeBtn.setOnClickListener(this);  
         deleteBtn.setOnClickListener(this);  
     }  
  
  private void initNFCData() {  
  // 初始化设备支持NFC功能 
         isNFC_support = true;  
  // 得到默认nfc适配器 
         nfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());  
  // 提示信息定义 
         String metaInfo = "";  
  // 判定设备是否支持NFC或启动NFC 
  if (nfcAdapter == null) {  
             metaInfo = "设备不支持NFC!";  
             Toast.makeText(this, metaInfo, Toast.LENGTH_SHORT).show();  
             isNFC_support = false;  
         }  
  if (!nfcAdapter.isEnabled()) {  
             metaInfo = "请在系统设置中先启用NFC功能!";  
             Toast.makeText(this, metaInfo, Toast.LENGTH_SHORT).show();  
             isNFC_support = false;  
         }  
  
  if (isNFC_support == true) {  
             init_NFC();  
         } else {  
             promt.setTextColor(Color.RED);  
             promt.setText(metaInfo);  
         }  
     }  
  
  @Override 
  public boolean onCreateOptionsMenu(Menu menu) {  
  // Inflate the menu; this adds items to the action bar if it is present. 
         getMenuInflater().inflate(R.menu.nfc_demo, menu);  
  return true;  
     }  
  
  // 字符序列转换为16进制字符串 
  private String bytesToHexString(byte[] src) {  
  return bytesToHexString(src, true);  
     }  
  
  private String bytesToHexString(byte[] src, boolean isPrefix) {  
         StringBuilder stringBuilder = new StringBuilder();  
  if (isPrefix == true) {  
             stringBuilder.append("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.toUpperCase(Character.forDigit(  
                     (src[i] >>> 4) & 0x0F, 16));  
             buffer[1] = Character.toUpperCase(Character.forDigit(src[i] & 0x0F,  
  16));  
             System.out.println(buffer);  
             stringBuilder.append(buffer);  
         }  
  return stringBuilder.toString();  
     }  
  
  private Tag tagFromIntent;  
  
  /** 
      * Parses the NDEF Message from the intent and prints to the TextView 
      */ 
  public void processIntent(Intent intent) {  
  if (isNFC_support == false)  
  return;  
  
  // 取出封装在intent中的TAG 
         tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);  
  
         promt.setTextColor(Color.BLUE);  
         String metaInfo = "";  
         metaInfo += "卡片ID:" + bytesToHexString(tagFromIntent.getId()) + "\n";  
         Toast.makeText(this, "找到卡片", Toast.LENGTH_SHORT).show();  
  
  // Tech List 
         String prefix = "android.nfc.tech.";  
         String[] techList = tagFromIntent.getTechList();  
  
  //分析NFC卡的类型: Mifare Classic/UltraLight Info 
         String CardType = "";  
  for (int i = 0; i < techList.length; i++) {  
  if (techList[i].equals(NfcA.class.getName())) {  
  // 读取TAG 
                 NfcA mfc = NfcA.get(tagFromIntent);  
  try {  
  if ("".equals(CardType))  
                         CardType = "MifareClassic卡片类型 \n 不支持NDEF消息 \n";  
                 } catch (Exception e) {  
                     e.printStackTrace();  
                 }  
             } else if (techList[i].equals(MifareUltralight.class.getName())) {  
                 MifareUltralight mifareUlTag = MifareUltralight  
                         .get(tagFromIntent);  
                 String lightType = "";  
  // Type Info 
  switch (mifareUlTag.getType()) {  
  case MifareUltralight.TYPE_ULTRALIGHT:  
                     lightType = "Ultralight";  
  break;  
  case MifareUltralight.TYPE_ULTRALIGHT_C:  
                     lightType = "Ultralight C";  
  break;  
                 }  
                 CardType = lightType + "卡片类型\n";  
  
                 Ndef ndef = Ndef.get(tagFromIntent);  
                 CardType += "最大数据尺寸:" + ndef.getMaxSize() + "\n";  
  
             }  
         }  
         metaInfo += CardType;  
         promt.setText(metaInfo);  
     }  
  
  // 读取方法 
  private String read(Tag tag) throws IOException, FormatException {  
  if (tag != null) {  
  //解析Tag获取到NDEF实例 
             Ndef ndef = Ndef.get(tag);  
  //打开连接 
             ndef.connect();  
  //获取NDEF消息 
             NdefMessage message = ndef.getNdefMessage();  
  //将消息转换成字节数组 
  byte[] data = message.toByteArray();  
  //将字节数组转换成字符串 
             String str = new String(data, Charset.forName("UTF-8"));  
  //关闭连接 
             ndef.close();  
  return str;  
         } else {  
             Toast.makeText(NfcDemoActivity.this, "设备与nfc卡连接断开,请重新连接...",  
                     Toast.LENGTH_SHORT).show();  
         }  
  return null;  
     }  
  
  // 写入方法 
  private void write(Tag tag) throws IOException, FormatException {  
  if (tag != null) {  
  //新建NdefRecord数组,本例中数组只有一个元素 
             NdefRecord[] records = { createRecord() };  
  //新建一个NdefMessage实例 
             NdefMessage message = new NdefMessage(records);  
  // 解析TAG获取到NDEF实例 
             Ndef ndef = Ndef.get(tag);  
  // 打开连接 
             ndef.connect();  
  // 写入NDEF信息 
             ndef.writeNdefMessage(message);  
  // 关闭连接 
             ndef.close();  
             promt.setText(promt.getText() + "写入数据成功!" + "\n");  
         } else {  
             Toast.makeText(NfcDemoActivity.this, "设备与nfc卡连接断开,请重新连接...",  
                     Toast.LENGTH_SHORT).show();  
         }  
     }  
  
  // 删除方法 
  private void delete(Tag tag) throws IOException, FormatException {  
  if (tag != null) {  
  //新建一个里面无任何信息的NdefRecord实例 
             NdefRecord nullNdefRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA,  
  new byte[] {}, new byte[] {}, new byte[] {});  
             NdefRecord[] records = { nullNdefRecord };  
             NdefMessage message = new NdefMessage(records);  
  // 解析TAG获取到NDEF实例 
             Ndef ndef = Ndef.get(tag);  
  // 打开连接 
             ndef.connect();  
  // 写入信息 
             ndef.writeNdefMessage(message);  
  // 关闭连接 
             ndef.close();  
             promt.setText(promt.getText() + "删除数据成功!" + "\n");  
         } else {  
             Toast.makeText(NfcDemoActivity.this, "设备与nfc卡连接断开,请重新连接...",  
                     Toast.LENGTH_SHORT).show();  
         }  
     }  
  
  //返回一个NdefRecord实例 
  private NdefRecord createRecord() throws UnsupportedEncodingException {  
  //组装字符串,准备好你要写入的信息 
         String msg = "BEGIN:VCARD\n" + "VERSION:2.1\n" + "中国湖北省武汉市\n" 
                 + "武汉大学计算机学院\n" + "END:VCARD";  
  //将字符串转换成字节数组 
  byte[] textBytes = msg.getBytes();  
  //将字节数组封装到一个NdefRecord实例中去 
         NdefRecord textRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA,  
  "text/x-vCard".getBytes(), new byte[] {}, textBytes);  
  return textRecord;  
     }  
  
  private MediaPlayer ring() throws Exception, IOException {  
  // TODO Auto-generated method stub 
         Uri alert = RingtoneManager  
                 .getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);  
         MediaPlayer player = new MediaPlayer();  
         player.setDataSource(this, alert);  
  final AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);  
  if (audioManager.getStreamVolume(AudioManager.STREAM_NOTIFICATION) != 0) {  
             player.setAudioStreamType(AudioManager.STREAM_NOTIFICATION);  
             player.setLooping(false);  
             player.prepare();  
             player.start();  
         }  
  return player;  
     }  
  
  private void startNFC_Listener() {  
  // 开始监听NFC设备是否连接,如果连接就发pi意图 
         nfcAdapter.enableForegroundDispatch(this, pi,  
  new IntentFilter[] { tagDetected }, null);  
     }  
  
  private void stopNFC_Listener() {  
  // 停止监听NFC设备是否连接 
         nfcAdapter.disableForegroundDispatch(this);  
     }  
  
  private void init_NFC() {  
  // 初始化PendingIntent,当有NFC设备连接上的时候,就交给当前Activity处理 
         pi = PendingIntent.getActivity(this, 0, new Intent(this, getClass())  
                 .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);  
  // 新建IntentFilter,使用的是第二种的过滤机制 
         tagDetected = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);  
         tagDetected.addCategory(Intent.CATEGORY_DEFAULT);  
     }  
  
 }  

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

Android NFC Demo1

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏向治洪

讯飞语音

、你需要android手机应用开发基础 2、科大讯飞语音识别SDK android版 3、科大讯飞语音识别开发API文档 4、android手机 关于科大讯飞S...

28010
来自专栏分享达人秀

Intent 属性详解(上)

Android应用将会根据Intent来启动指定组件,至于到底启动哪个组件,则取决于Intent的各属性。本期将详细介绍Intent的各属性值,以及 A...

19610
来自专栏Android-JessYan

我一行代码都不写实现Toolbar!你却还在封装BaseActivity?

原文地址: http://www.jianshu.com/p/75a5c24174b2 qq群:301733278

1344
来自专栏Android先生

我一行代码都不写实现Toolbar!你却还在封装BaseActivity?

距离 上篇文章 的发表时间已经过去两个多月了,这两个月时间里我没写文章但一直在更新着我的 MVPArms 框架,让他逐渐朝着 可配置化集成框架 发展

611
来自专栏刘望舒

RxBinding使用和源码解析

作者 | juexingzhe 地址 | https://www.jianshu.com/u/ea71bb3770b4 声明 | 本文是 juexingzhe...

43610
来自专栏GIS讲堂

Arcgis for Androd API开发系列教程(一)——地图显示与GPS定位

序:最近呢,工作鸭梨不是怎么大,对于自己爱折腾的想法又冒出了水面,开始自己的android开发的学习之旅。但是呢,本人是做GIS的,所以呢,就打算从这方面入手看...

1375
来自专栏Android先生

Context都没弄明白,还怎么做Android开发?

作为Android开发者,不知道你有没有思考过这个问题,Activity可以new吗?Android的应用程序开发采用JAVA语言,Activity本质上也是一...

882
来自专栏潇涧技术专栏

Android Dependency Injection Libraries

本文总结并对比了三种Android依赖注入库:Butter Knife、RoboGuice、Android Annotations的使用

871
来自专栏飞雪无情的博客

Android EditText使用详解-包含很多教程上看不到的功能演示

标题有点大,说是详解,其实就是对EditText的一些常用功能的介绍,包括密码框,电话框,空白提示文字等等的讲解,尽量的介绍详细一点,也就是所谓的详解了。。呵呵

2732
来自专栏GIS讲堂

Arcgis andoid开发之应用百度地图接口实现精准定位与显示

怀着激动、兴奋的心情,在这个漫天柳絮的季节写下了这片博文,为什么呢,因为困扰我很久的一个技术性的问题得到了解决,发次博文,供大家参观、学习,同时,也以慰藉我长期...

1203

扫码关注云+社区