您的位置:首页 > 移动开发 > Android开发

Android之NFC

2016-03-23 11:37 393 查看
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

<?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的内容如下,包括读取、写入、删除三大功能:(其中删除功能是通过写入空值来实现的)

[java] view
plain copy

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

/article/7977095.html

/article/2777520.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: