前言

NFC 是 Near Field Communication 缩写,即近距离无线通讯技术。可以在移动设备、消费类电子产品、PC 和智能控件工具间进行近距离无线通信。

NFC 工作模式

NFC 工作模式主要有三种工作模式,分别是卡模式(Card emulation)、点对点模式(P2P mode)和读卡器模式(Reader/writer 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会直接利用蓝牙传输。这种技术被称为 AndroidBeam。所以使用 androidBeam 传输数据的两部设备不再限于4厘米之内。
    【Android -- 实战】NFC 的基本使用_Android

准备工作

1. 在清单文件中,设置权限和配置 ​​launchMode​​ 属性

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

...
<activity
android:name=".ui.RunUrlActivity"
android:exported="false"
android:launchMode="singleTop" />

注意:通常来说,所有处理 ​​NFC​​​ 的 ​​Activity​​​ 都要设置 ​​launchMode​​​ 属性为 ​​singleTop​​​ 或者 ​​singleTask​​​,保证了无论 ​​NFC​​​ 标签靠近手机多少次,​​Activity​​ 实例只有一个。

2. BaseNfcActivity.java

public abstract class BaseNfcActivity extends AppCompatActivity {
private NfcAdapter mNfcAdapter;
private PendingIntent mPendingIntent;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View view = LayoutInflater.from(this).inflate(getLayoutId(), null);
setContentView(view);
ButterKnife.bind(this);
initView();
}

protected abstract int getLayoutId();

protected abstract void initView();

/**
* 启动Activity,界面可见时
*/
@Override
protected void onStart() {
super.onStart();
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
//一旦截获NFC消息,就会通过PendingIntent调用窗口
mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()), 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);
}

/**
* 不带参数的跳转
*
* @param clazz 跳转到的目标类
*/
protected void readyGo(final Class<?> clazz) {
Intent intent = new Intent(this, clazz);
startActivity(intent);
}

/**
* 带参数的跳转
*
* @param clazz 跳转到的目标类
* @param bundle 参数
*/
protected void readyGo(final Class<?> clazz, final Bundle bundle) {
Intent intent = new Intent(this, clazz);
if (bundle != null) {
intent.putExtras(bundle);
}
startActivity(intent);
}
}

简单实例

场景:现将应用程序的包写到 ​​NFC​​​ 程序上,然后我们将 ​​NFC​​​ 标签靠近 ​​Android​​​ 手机,手机就会自动运行包所对应的程序,这个是 ​​NFC​​​ 比较基本的一个应用。下面以贴近标签自动运行 ​​Android​​​ 自带的“短信”为例。
1. 获取 Tag 对象

@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (mPackageName == null)
return;
//1.获取Tag对象
Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
writeNFCTag(detectedTag);
}

2. 判断 ​​NFC​​ 标签的数据类型(通过 ​​Ndef.get​​ 方法)

//2.判断NFC标签的数据类型(通过Ndef.get方法)
Ndef ndef = Ndef.get(tag);

3. 写入数据

//3.写入数据
ndef.writeNdefMessage(ndefMessage);

完整代码

public class RunAppActivity extends BaseNfcActivity {
private String mPackageName = "com.android.mms";//短信

@BindView(R.id.topbar)
QMUITopBar mTopBar;

@Override
protected int getLayoutId() {
return R.layout.activity_run_app;
}

@Override
protected void initView() {
mTopBar.setTitle("自动运行程序");
}

@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (mPackageName == null)
return;
//1.获取Tag对象
Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
writeNFCTag(detectedTag);
}

/**
* 往标签写数据的方法
*
* @param tag
*/
public void writeNFCTag(Tag tag) {
if (tag == null) {
return;
}
NdefMessage ndefMessage = new NdefMessage(new NdefRecord[]{NdefRecord
.createApplicationRecord(mPackageName)});
//转换成字节获得大小
int size = ndefMessage.toByteArray().length;
try {
//2.判断NFC标签的数据类型(通过Ndef.get方法)
Ndef ndef = Ndef.get(tag);
//判断是否为NDEF标签
if (ndef != null) {
ndef.connect();
//判断是否支持可写
if (!ndef.isWritable()) {
return;
}
//判断标签的容量是否够用
if (ndef.getMaxSize() < size) {
return;
}
//3.写入数据
ndef.writeNdefMessage(ndefMessage);
Toast.makeText(this, "写入成功", Toast.LENGTH_SHORT).show();
} else { //当我们买回来的NFC标签是没有格式化的,或者没有分区的执行此步
//Ndef格式类
NdefFormatable format = NdefFormatable.get(tag);
//判断是否获得了NdefFormatable对象,有一些标签是只读的或者不允许格式化的
if (format != null) {
//连接
format.connect();
//格式化并将信息写入标签
format.format(ndefMessage);
Toast.makeText(this, "写入成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "写入失败", Toast.LENGTH_SHORT).show();
}
}
} catch (Exception e) {
}
}
}

测试方法:
将 ​​​NFC​​​ 标签贴近手机背面,自动写入数据,此时退出所有程序,返回桌面,然后再将 ​​NFC​​ 标签贴近手机背面,将会看到自动打开了“短信”。