前段时间由于工作需要,对NFC做了一些学习以及项目上的应用,最近工作不是很忙,给大家更新一下NFC连载篇,大家可以共同学习进步!
NFC开发 —————实现NFC手机做门禁卡的方法(二)
NFC开发 —————ID卡、IC卡(M1卡、CPU卡)的区别(三)
NFC开发 —————实用工具以及开发文档(四)
简介:即近距离无线通讯技术。这个技术由免接触式射频识别(RFID)演变而来,由飞利浦半导体(现恩智浦半导体)、诺基亚和索尼共同研制开发,其基础是RFID及互连技术。近场通信是一种短距高频的无线电技术,在13.56MHz频率运行于20厘米距离内。其传输速度有106 Kbit/秒、212 Kbit/秒或者424 Kbit/秒三种。目前近场通信已通过成为ISO/IEC IS 18092国际标准、EMCA-340标准与ETSI TS 102 190标准。
数据在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、蓝牙和红外之间的差异:
后期会专门出一篇文章对卡片分类做一些讲解(ID卡、IC卡(M1卡、CPU卡)的区别)
卡模拟模式可实现的应用
1、基于HCE应用
1、)公交卡(虚拟卡)
软件厂家与交通部等联合推出的交通联合标准的公交云卡。如:福建出行助手。
2、)银行卡(虚拟卡)
各银行APP开通的云闪付,联机闪付。如:银行APP的云闪付。
2、基于NFC-SWP应用(电信钱包/移动和包/联通沃钱包)
1、)公交卡(实体异形卡)
当地运营商与各城市一卡通公司合作开通的NFC公交应用。如:成都电信的手机天府通。
2、)银行卡(实体异形卡,已没落。)
运营商与各银行或银联开通的银行卡应用。如:电子借记卡/贷记卡,联机闪付;电子现金卡,脱机闪付。
3、)门禁卡/消费卡(实体异形卡)
运营商与各学校或企业合作开通的手机一卡通,直接用NFC手机代替校园卡或企业卡等。如:电信的校园翼机通。
3、基于eSE应用
1、)公交卡(实体异形卡)
手机厂家与部分城市一卡通合作开通的公交卡。如:Mi Pay、Huawei Pay等支持开通部分城市公交卡。
2、)银行卡(虚拟卡)
手机厂家与银联及银行合作开通的银行卡。如:Apple Pay、Sumsung Pay、huawei Pay、Mi Pay等绑定银行卡开通的虚拟卡,联机闪付。
读写模式可实现的应用(常用)
1、)都都宝手机客户端
天府通卡的电子钱包充值。
2、)卡卡联手机客户端
可为银行闪付卡(金融IC卡)的电子现金账户充值(圈存)。
3、)银联钱包的‘生活’中‘拍拍卡’
可为银行闪付卡(金融IC卡)的电子现金账户充值(圈存)。
非主流卡模拟应用(基于NFC-SD应用)
1、)公交手环或手表,如刷刷手环、怡康(Walker)手环等。
2、)银行手环或手表,如兴动力手环等。
3、)公交及银行手环或手表,如拉卡拉手环或手表。
Android的NFC标签调度系统
当手机发现外部NFC的标签(指含有NFC功能的设备)时,Android系统会寻找可以处理这个标签的Activity,那怎么知道哪个Activity能处理这条NFC消息呢?答案是清单文件,我们需要在清单文件中设置intent-filter。系统会分发NFC消息到设置intent-filter的Activity中,当然,接收NFC消息也有优先级之分,也是通过设置intent-filter来设置接收NFC消息的优先级的。
NFC的标签调度系统绑定了3中intent,按优先级的高低列出,如下
卡片常用的数据格式:
MifareClassic数据格式就是NfcA。
IsoDep:各种公交卡。
NfcB:二代身份证。
NfcF:Felica。
NfcV:德州仪器的VicinityCard
Ndef:安卓主流的传输数据格式。
最后这里简单的教大家如何使用NFC
NFC集成以及配置
清单文件里面的配置:
<uses-permission android:name="android.permission.NFC" /> <!-- 当前设备必须要有NFC芯片 -->
<uses-feature
android:name="android.hardware.nfc"
android:required="true" />
对执行读取设备数据的activity配置:
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
</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" />
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED" />
</intent-filter>
nfc_tech_filter:
<?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>
我在做NFC的时候,对其抽了一个基类:
/**
* @author Martin-harry
* @date 2021/11/11
* @address
* @Desc 子类在onNewIntent方法中进行NFC标签相关操作。
* 在onNewIntent方法中执行intent传递过来的Tag数据
*/
public abstract class BaseNfcActivity extends FragmentActivity {
protected NfcAdapter mNfcAdapter;
private PendingIntent mPendingIntent;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(createViews());//初始化视图
initView();//初始化控件
initData();//初始化数据
}
protected abstract void initView();
protected abstract void initData();
protected abstract int createViews();
/**
* onCreat->onStart->onResume->onPause->onStop->onDestroy
* 启动Activity,界面可见时.
*/
@Override
protected void onStart() {
super.onStart();
//此处adapter需要重新获取,否则无法获取message
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
//一旦截获NFC消息,就会通过PendingIntent调用窗口
mPendingIntent = PendingIntent.getActivity(this, 0,
new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
}
/**
* 获得焦点,按钮可以点击
*/
@Override
public void onResume() {
super.onResume();
//设置处理优于所有其他NFC的处理
if (mNfcAdapter != null)
mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
}
/**
* 暂停Activity,界面获取焦点,按钮可以点击
*/
@Override
public void onPause() {
super.onPause();
//恢复默认状态
if (mNfcAdapter != null)
mNfcAdapter.disableForegroundDispatch(this);
}
}
对用户是否已经开启NFC权限处理
/**
* @author Martin-harry
* @date 2021/11/11
* @address
* @Desc WelcomeActivity
*/
public class WelcomeActivity extends BaseActivity implements View.OnClickListener {
private ImageView nfc;
private NfcAdapter mNfcAdapter;
@Override
protected void initView() {
nfc = findViewById(R.id.nfc);
nfc.setOnClickListener(this);
}
@Override
protected void initData() {
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
}
@Override
protected int createViews() {
return R.layout.activity_welcome;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.nfc:
if (!ifNFCUse()) {
Intent setNfc = new Intent(Settings.ACTION_NFC_SETTINGS);
startActivity(setNfc);
} else {
startActivity(new Intent(this, ReadNfcActivity.class));
}
break;
}
}
/**
* 检测工作,判断设备的NFC支持情况
*
* @return
*/
protected Boolean ifNFCUse() {
if (mNfcAdapter == null) {
Toast.makeText(this, "当前设备不支持NFC!", Toast.LENGTH_SHORT).show();
return false;
}
if (mNfcAdapter != null && !mNfcAdapter.isEnabled()) {
Toast.makeText(this, "请在系统设置中先启用NFC功能!", Toast.LENGTH_SHORT).show();
return false;
}
return true;
}
}
下面就是读取NFC设备数据的操作:
/**
* @author Martin-harry
* @date 2021/11/11
* @address
* @Desc NFC读取
*/
public class ReadNfcActivity extends BaseNfcActivity implements View.OnClickListener {
private TextView mNfcText;
private ImageView back;
private LinearLayout linTxt;
private ImageView readPic;
private TextView systemTime;
private Button bt;
private LinearLayout linBt;
private String currentTime;
private Tag mTag;
@Override
protected void initView() {
back = findViewById(R.id.back);
readPic = findViewById(R.id.readPic);
linTxt = findViewById(R.id.linTxt);
mNfcText = findViewById(R.id.nfcTxt);
systemTime = findViewById(R.id.systemTime);
linBt = findViewById(R.id.linBt);
bt = findViewById(R.id.bt);
back.setOnClickListener(this);
bt.setOnClickListener(this);
resolveIntent(getIntent());
}
@Override
public void onNewIntent(Intent intent) {
if (mNfcAdapter != null) {
if (mNfcAdapter.isEnabled()) {
resolveIntent(getIntent());
}
}
}
/**
* 初次判断卡片的类型
*
* @param intent
*/
private void resolveIntent(Intent intent) {
NdefMessage[] msgs = NfcUtil.getNdefMsg(intent); //解析nfc标签中的数据
if (msgs == null) {
Toast.makeText(ReadNfcActivity.this, "非NFC启动", Toast.LENGTH_SHORT).show();
} else {
String id = NfcUtil.readNFCId(NfcUtil.getNFCTag(intent));
Log.e("读取数据 >>>", "nfcID:" + id);
setNFCMsgView(id, msgs);
}
}
/**
* 显示扫描后的信息
*
* @param ndefMessages ndef数据
*/
@SuppressLint("SetTextI18n")
private void setNFCMsgView(String tag, NdefMessage[] ndefMessages) {
if (ndefMessages == null || ndefMessages.length == 0) {
return;
}
// mNfcText.setText("Payload:" + new String(ndefMessages[0].getRecords()[0].getPayload()) + "\n");
Log.e("ndef数据 >>>>", "setNFCMsgView ndefMessages: " + ndefMessages.length );
Log.e("ndef数据 >>>>", "setNFCMsgView Payload: " + new String(ndefMessages[0].getRecords()[0].getPayload()) );
readPic.setVisibility(View.GONE);
linTxt.setVisibility(View.VISIBLE);
linBt.setVisibility(View.VISIBLE);
//将数据信息存储到本地
try {
FileOutputStream fos = new FileOutputStream(getExternalFilesDir(null) + "/NFC读取.txt", true);
OutputStreamWriter ost = new OutputStreamWriter(fos);
NdefMessage msg = ndefMessages[0];
List<ParsedNdefRecord> records = NdefMessageParser.parse(msg);
final int size = records.size();
Log.e("ndef数据 >>>>", "setNFCMsgView records: " + size );
for (int i = 0; i < size; i++) {
ParsedNdefRecord record = records.get(i);
String math = record.toString();
Log.e("ndef数据 >>>>", "setNFCMsgView: math" + math );
String viewText = record.getViewText();
String TagStr = "55AA" + viewText;
mNfcText.setText("Tag ID (hex): " + tag + "\n"
+ "message:" + TagStr);
currentTime = TimeFormatUtils.getCurrentTime();
systemTime.setText("当前系统时间:" + currentTime);
Log.e("扫描后的数据信息 >>>>", "setNFCMsgView: " + record.getViewText());
ost.write("Tag ID(hex):" + record.getViewText());
ost.write("当前系统时间:" + currentTime);
ost.write("\n");
ost.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//字符序列转换为16进制字符串
private static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("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.forDigit((src[i] >>> 4) & 0x0F, 16);
buffer[1] = Character.forDigit(src[i] & 0x0F, 16);
stringBuilder.append(buffer);
}
return stringBuilder.toString();
}
@Override
protected void initData() {
}
@Override
protected int createViews() {
return R.layout.activity_read_nfc;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.back:
finish();
break;
case R.id.bt:
mNfcText.setText("");
systemTime.setText("");
finish();
break;
}
}
}