Android硬件通信之 NFC通信

一,简介

​​​​​​​1.1 NFC是继wifi和蓝牙之后,又一种新的近距离无线传输技术,这种局限性比较大,设备必须装有NFC硬件才能实现数据传输,而且传输距离非常短,4cm之内才能传输,真实场景可能触碰才能传输。

1.2 NFC优缺点

优点:传输速度快;连接方便,触碰就能实现通信;安全性高

缺点:传输距离非常短;传输数据量小;并不是全部普及,部分手机不支持

1.3 NFC,蓝牙,红外对比

对比项 NFC 蓝牙 红外
网络类型 点对点 单点对多点 点对点
有效距离 <=0.1m <=10m,最新的蓝牙4.0有效距离可达100m 一般在1m以内,热技术连接,不稳定
传输速度 最大424kbps 最大24Mbps 慢速115.2kbp5,快速4Mbps
连接时间 <0.1s 6s 0.5s
安全性 安全,硬件实现 安全,软件实现 个安全,使用IRFM时除外
通信模式 主动-主动/被动 主动-主动 主动-主动
成本

1.4 NFC通信模式

读卡器模式(Reader/writer mode)、仿真卡模式(Card Emulation Mode)、点对点模式(P2P mode)。

读卡器模式

数据在NFC芯片中,可以简单理解成“刷标签”。本质上就是通过支持NFC的手机或其它电子设备从带有NFC芯片的标签、贴纸、名片等媒介中读写信息。通常NFC标签是不需要外部供电的。当支持NFC的外设向NFC读写数据时,它会发送某种磁场,而这个磁场会自动的向NFC标签供电。

仿真卡模式

数据在支持NFC的手机或其它电子设备中,可以简单理解成“刷手机”。本质上就是将支持NFC的手机或其它电子设备当成借记卡、公交卡、门禁卡等IC卡使用。基本原理是将相应IC卡中的信息凭证封装成数据包存储在支持NFC的外设中 。

在使用时还需要一个NFC射频器(相当于刷卡器)。将手机靠近NFC射频器,手机就会接收到NFC射频器发过来的信号,在通过一系列复杂的验证后,将IC卡的相应信息传入NFC射频器,最后这些IC卡数据会传入NFC射频器连接的电脑,并进行相应的处理(如电子转帐、开门等操作)。

点对点模式

该模式与蓝牙、红外差不多,用于不同NFC设备之间进行数据交换,不过这个模式已经没有有“刷”的感觉了。其有效距离一般不能超过4厘米,但传输建立速度要比红外和蓝牙技术快很多,传输速度比红外块得多,如过双方都使用Android4.2,NFC会直接利用蓝牙传输。这种技术被称为Android Beam。所以使用Android Beam传输数据的两部设备不再限于4厘米之内。

点对点模式的典型应用是两部支持NFC的手机或平板电脑实现数据的点对点传输,例如,交换图片或同步设备联系人。因此,通过NFC,多个设备如数字相机,计算机,手机之间,都可以快速连接,并交换资料或者服务。

二,NFC通信步骤

2.1 AndroidManifest.xml添加NFC使用权限,不用动态请求

<!--NFC 相关权限-->
<!--描述所需硬件特性-->
<uses-feature
	android:name="android.hardware.nfc"
	android:required="true" />
<uses-permission android:name="android.permission.NFC" />

2.2 添加NFC标签过滤,两种方式

方式一:AndroidManifest.xml的Activity标签添加xml配置

res目录下 新建xml文件夹,再新建nfc_tech_filter.xml文件

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <!-- 可以处理所有Android支持的NFC类型 -->
    <tech-list>
        <tech>android.nfc.tech.IsoDep</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcA</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcB</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcF</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NfcV</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.Ndef</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.NdefFormatable</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.MifareUltralight</tech>
    </tech-list>
    <tech-list>
        <tech>android.nfc.tech.MifareClassic</tech>
    </tech-list>
</resources>

activity标签配置该xml

<activity
	android:name=".MainActivity"
	android:exported="true">
	<intent-filter>
		<action android:name="android.intent.action.MAIN" />

		<category android:name="android.intent.category.LAUNCHER" />
	</intent-filter>

	<intent-filter>
		<action android:name="android.nfc.action.TECH_DISCOVERED" />
	</intent-filter>
	<meta-data
		android:name="android.nfc.action.TECH_DISCOVERED"
		android:resource="@xml/nfc_tech_filter" />

</activity>

 方式二:初始化NFC时候动态配置标签

/**
 * 初始化nfc设置
 */
public static void NfcInit(Activity activity) {
	Intent intent = new Intent(activity, activity.getClass());
	intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
	mPendingIntent = PendingIntent.getActivity(activity, 0, intent, 0);
	//做一个IntentFilter过滤你想要的action 这里过滤的是ndef
	IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
	//如果你对action的定义有更高的要求,比如data的要求,你可以使用如下的代码来定义intentFilter
	//        IntentFilter filter2 = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
	//        try {
	//            filter.addDataType("*/*");
	//        } catch (IntentFilter.MalformedMimeTypeException e) {
	//            e.printStackTrace();
	//        }
	//        mIntentFilter = new IntentFilter[]{filter, filter2};
	//        mTechList = null;
	  try {
			filter.addDataType("*/*");
	  } catch (IntentFilter.MalformedMimeTypeException e) {
			e.printStackTrace();
	  }
	mTechList = new String[][]{
   
   {MifareClassic.class.getName()},
		{NfcA.class.getName()}};
	//生成intentFilter
	mIntentFilter = new IntentFilter[]{filter};
}

2.3 创建NFC适配器

NFC所有的操作都是通过NfcAdapter完成的,创建NfcAdapter后,同时新建一个等待的PendingIntent,这个主要用于出发NFC打开该页面

private NfcAdapter mNfcAdapter;
private PendingIntent mPendingIntent;
@Override
protected void onStart() {
	super.onStart();
	mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
	mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass())
			.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);

}

2.4 在onResume中开启前台调度,来初始化NFC

//在onResume中开启前台调度
@Override
protected void onResume() {
	super.onResume();
	//设定intentfilter和tech-list。如果两个都为null就代表优先接收任何形式的TAG action。也就是说系统会主动发TAG intent。
	if (mNfcAdapter != null) {
		mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null); //启动        }
	}
}

2.5 在onNewIntent中处理由NFC设备传递过来的intent


//在onNewIntent中处理由NFC设备传递过来的intent
@Override
protected void onNewIntent(Intent intent) {
	super.onNewIntent(intent);
	processIntent(intent);
}

2.6 处理读取过来的数据,包括卡ID(通常为设备序列号,16进制形式09:D1:E6:6B)

然后NFC卡其它信息不一定有,NFC卡标签这些信息可能是没有的,但序列号是一定有的,即NFC卡的唯一标识

//这块的processIntent() 就是处理卡中数据的方法
public void processIntent(Intent intent) {
	try {
		tvContent.setText("");
		// 检测卡的id
		String id = readNFCId(intent);
		// NfcUtils中获取卡中数据的方法
		String result = readNFCFromTag(intent);
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.append("卡ID十六进制:" + id).append("\r\n");
		stringBuilder.append("卡ID十进制:" + hexToDec(id)).append("\r\n");
		stringBuilder.append("信息:").append("\r\n");
		stringBuilder.append(result).append("\r\n");


		tvContent.setText(stringBuilder);
		
		// 往卡中写数据
		//String data = "this.is.write";
		//writeNFCToTag(data, intent);
	} catch (Exception e) {
		e.printStackTrace();
	}
}

/**
 * 读取nfcID
 */
public String readNFCId(Intent intent) throws UnsupportedEncodingException {
	Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
	String id = ByteArrayToHexString(tag.getId());
	return id;
}


/**
 * 读取NFC的数据
 */
public String readNFCFromTag(Intent intent) throws UnsupportedEncodingException {
	Parcelable[] rawArray = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
	StringBuilder stringBuilder = new StringBuilder();
	if (rawArray != null) {
		for (int i = 0; i < rawArray.length; i++) {
			NdefMessage mNdefMsg = (NdefMessage) rawArray[i];

			for (int j = 0; j < mNdefMsg.getRecords().length; i++) {
				NdefRecord mNdefRecord = mNdefMsg.getRecords()[j];

				if (mNdefRecord != null) {
					String readResult = new String(mNdefRecord.getPayload(), "UTF-8");
					stringBuilder.append(readResult).append("\r\n");
				}
			}
		}
	}
	return stringBuilder.toString();
}


/**
 * 十六进制转10进制
 * @param s
 * @return
 */
public static int hexToDec(String s) {
	String s1 = s.toUpperCase(); // 全转大写
	char[] chars = s1.toCharArray(); // 转成 char 数组
	Stack<Character> stack = new Stack<>();
	for (int i = 0; i < chars.length; i++) {
		stack.push(chars[i]); // 放入栈中,倒序遍历
	}
	int sum = 0;  // 定义总和
	int size = stack.size(); // 要先赋值给 size ,不然 stack.pop() 之后 size 会变
	for (int i = 0; i < size; i++) {
		Character pop = stack.pop();
		if (String.valueOf(pop).matches("[A-F]")) {  // 如果是 A-F
			sum += (Math.pow(16, i) * ((pop - 55))); // A的ASCII码为 65,取偏移量
		} else { // 如果是纯数字
			sum += Math.pow(16, i) * Integer.parseInt(String.valueOf(pop));
		}
	}
	return sum;
}


/**
 * 将字节数组转换为字符串
 */
private String ByteArrayToHexString(byte[] inarray) {
	int i, j, in;
	String[] hex = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"};
	String out = "";

	for (j = 0; j < inarray.length; ++j) {
		in = (int) inarray[j] & 0xff;
		i = (in >> 4) & 0x0f;
		out += hex[i];
		i = in & 0x0f;
		out += hex[i];
	}
	return out;
}

2.7 往NFC写入数据

/**
 * 往nfc写入数据
 */
public static void writeNFCToTag(String data, Intent intent){
	try {
		Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
		Ndef ndef = Ndef.get(tag);
		ndef.connect();
		NdefRecord ndefRecord = null;
		if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
			ndefRecord = NdefRecord.createTextRecord(null, data);
		}
		NdefRecord[] records = {ndefRecord};
		NdefMessage ndefMessage = new NdefMessage(records);
		ndef.writeNdefMessage(ndefMessage);
	}catch (Exception e){

	}
}
// 往卡中写数据
String data = "this.is.write";
writeNFCToTag(data, intent);

 2.8 取消NfcAdapter调度

@Override
protected void onPause() {
	super.onPause();
	if (mNfcAdapter != null) {
		mNfcAdapter.disableForegroundDispatch(this);
	}
}

2.9 销毁NfcAdapter

@Override
protected void onDestroy() {
	super.onDestroy();
	mNfcAdapter = null;
}

2.10 总结

NFC适合卡片类刷卡数据传输场景,比如门禁卡,支付卡,地铁卡等,快捷方便

猜你喜欢

转载自blog.csdn.net/qq_29848853/article/details/130278294
今日推荐