一.功能需求
当手机检测到NFC标签后,可打开相应应用,不管应用是在后台还是不在后台都可打开,当NFC标签离开手机时,会有相应的提示。
二.开发环境
Android studio
具有NFC功能的手机
NFC标签纸或NFC卡
三.实现步骤
1.首先在AndroidManifest.xml文件中添加如下配置,分别指定安卓SDK版本,NFC权限,要求当前设备必须要有NFC芯片
<uses-sdk android:minSdkVersion="10" />
<uses-permission android:name="android.permission.NFC" />
<uses-feature
android:name="android.hardware.nfc"
android:required="true" />
2.使用intent-filter过滤NFC标签,有三种过滤方式ACTION_NDEF_DISCOVERED,ACTION_TECH_DISCOVERED,ACTION_TAG_DISCOVERED
可同时配置。如下
<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.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFUALT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
<category android:name="android.intent.category.DEFUALT" />
<data android:resource="@xml/nfc_tech_filter" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="vnd.android.nfc"
android:host="ext"
android:pathPrefix="/com.example.nfc_test"/>
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED"/>
</intent-filter>
其中ACTION_TECH_DISCOVERED需要自己新建过滤器nfc_tech_filter.xml,过滤NFC卡的类型如下:
<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>
View Code
完整AndroidManifest.xml如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.nfc_test">
<uses-sdk android:minSdkVersion="10" />
<uses-permission android:name="android.permission.NFC" />
<uses-feature
android:name="android.hardware.nfc"
android:required="true" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.NFC_TEST">
<meta-data
android:name="com.google.android.actions"
android:resource="@xml/nfc_tech_filter" />
<activity
android:name=".MainActivity"
android:launchMode="singleTop">
<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.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFUALT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
<category android:name="android.intent.category.DEFUALT" />
<data android:resource="@xml/nfc_tech_filter" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="vnd.android.nfc"
android:host="ext"
android:pathPrefix="/com.example.nfc_test"/>
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED"/>
</intent-filter>
</activity>
</application>
</manifest>
View Code
3.创建一个基类BaseNfcActivity
package com.example.nfc_test;
import android.app.PendingIntent;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class BaseNfcActivity extends AppCompatActivity {
public NfcAdapter mNfcAdapter;
public PendingIntent mPendingIntent;
private String TAG = "Android";
@Override
protected void onStart() {
super.onStart();
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if(mNfcAdapter == null){
//to to sth..
return;
}
else{
if(!mNfcAdapter.isEnabled()){
Intent nfcSet = new Intent(Settings.ACTION_NFC_SETTINGS);
startActivity(nfcSet);
}
}
// 用于感应到NFC时启动该Activity
// 这里建议将处理NFC的子类的launchMode设置成singleTop模式,这样感应到标签时就会回调onNewIntent,而不会重复打开页面
mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()), 0);
}
/**
* 获得焦点,按钮可以点击
*/
/* @Override
public void onResume() {
super.onResume();
Log.d(TAG, "bbbbbbbbbbbbbbbbbbbbbbb");
// 设置当该页面处于前台时,NFC标签会直接交给该页面处理
if (mNfcAdapter != null) {
// Toast.makeText(getApplicationContext(),"NFC标签已离开",Toast.LENGTH_SHORT).show();
mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
}
}*/
/**
* 暂停Activity,界面获取焦点,按钮可以点击
*/
@Override
public void onPause() {
super.onPause();
// 当页面不可见时,NFC标签不交给当前页面处理
if (mNfcAdapter != null) {
mNfcAdapter.disableForegroundDispatch(this);
}
}
}
View Code
4.创建主活动窗口类MainActivity继承BaseNfcActivity
package com.example.nfc_test;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.nfc.FormatException;
import android.os.Bundle;
import android.content.Intent;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.nfc.tech.NdefFormatable;
import android.os.Bundle;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
/*
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}*/
public class MainActivity extends BaseNfcActivity {
private String TAG = "Debug";
public boolean flag = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
// 将Activity的launchMode设置成singleTop,这样当感应到NFC标签时不会重复打开页面,而是回调该方法
@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
String action = intent.getAction();
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)// NDEF类型
|| NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)// 其他类型
|| NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) {
// get tag
Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
String CardId = ByteArrayToHexString(detectedTag.getId());
Log.d(TAG, "ID:" + CardId);
writeTag(detectedTag);
} else {
//...
}
//write tag
// writeTag(detectedTag);
}
/**
* 写标
*
* @param tag
*/
public void writeTag(Tag tag) {
if (tag == null) {
return;
}
// 利用应用包名创建待写入的数据
String mPackageName = "com.example.nfc_test";
NdefMessage ndefMessage = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
ndefMessage = new NdefMessage(new NdefRecord[]{NdefRecord
.createApplicationRecord(mPackageName)});
}
// NdefMessage ndefMessage = new NdefMessage(new NdefRecord[]{NdefRecord
// .createUri(Uri.parse("http://www.baidu.com"))});
// 获取内容字节大小
int size = ndefMessage.toByteArray().length;
try {
// 获取Nedf
Ndef ndef = Ndef.get(tag);
// 不为空表示该标签为Nedf格式
if (ndef != null && (!ndef.isConnected())) {
ndef.connect();
// 是否可写
if (!ndef.isWritable()) {
Toast.makeText(this, "标签不支持写入", Toast.LENGTH_SHORT).show();
return;
}
// 判断写入内容大小是否超出允许写入的最大值
if (ndef.getMaxSize() < size) {
Toast.makeText(this, "写入内容过大", Toast.LENGTH_SHORT).show();
return;
}
// 写入数据
ndef.writeNdefMessage(ndefMessage);
ndef.close();
Toast.makeText(this, "写入成功", Toast.LENGTH_SHORT).show();
} else { // 标签非Nedf格式的情况
NdefFormatable format = NdefFormatable.get(tag);
// 不为空表示该标签允许格式化成Ndef格式
if (format != null) {
format.connect();
// 格式化并写入Nedf内容
format.format(ndefMessage);
Toast.makeText(this, "写入成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "标签不支持Nedf格式", Toast.LENGTH_SHORT).show();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
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;
}
@Override
public void onResume() {
super.onResume();
// 设置当该页面处于前台时,NFC标签会直接交给该页面处理
if (mNfcAdapter != null) {
// Toast.makeText(getApplicationContext(),"NFC标签已离开",Toast.LENGTH_SHORT).show();
mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
LoopCheckNfc();
}
}
public void LoopCheckNfc() {
Intent intent = getIntent();
Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (detectedTag != null) {
Ndef ndef = Ndef.get(detectedTag);
new Thread(new Runnable() {
public void run() {
try {
while (true) {
try {
Thread.sleep(1000);
if (ndef != null && (!ndef.isConnected())) {
ndef.connect();
NdefMessage msg = ndef.getNdefMessage();
// TODO: do something
}
} catch (IOException e) {
// if the tag is gone we might want to end the thread:
Looper.prepare();
Toast.makeText(getApplicationContext(), "NFC标签已不存在,请确认!", Toast.LENGTH_SHORT).show();
Looper.loop();
break;
} catch (FormatException e) {
e.printStackTrace();
} finally {
try {
ndef.close();
} catch (Exception e) {
}
}
}
} catch (InterruptedException e) {
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
else
{
Log.d(TAG, "invalid pointer!");
}
}
}
View Code
综上就是NFC功能打开指定应用的实现。以上只为简单记录,如有错误之处望指出。