目录
NFC的全称是"Near Field Communication",意思是近场通信、与邻近的区域通信。
1.申请权限
html
<!-- NFC权限,无需动态申请 -->
<uses-permission android:name="android.permission.NFC" />
2.设置意图过滤器
在清单文件(AndroidManifests)中为Activity设置意图过滤器(IntentFilter);在程序检测到NFC操作时将跳转至该界面。
本部分操作用于从外部直接打开指定Activity。
- android.nfc.action.NDEF_DISCOVERED :指定了当应用程序收到包含NDEF(NFC Data Exchange Format)数据的NFC标签时应该触发的动作。NDEF是一种用于在NFC设备之间交换信息的标准格式。
- android.nfc.action.TAG_DISCOVERED :定义了当检测到NFC标签时应该触发的动作。这个动作用于处理未包含NDEF数据的NFC标签。
- android.nfc.action.TECH_DISCOVERED :指定了当检测到支持的NFC技术时应该触发的动作。这允许应用程序处理特定的NFC技术。其中具体的NFC技术由meta-data标签指定源文件。
html
<!--AndroidManifests-->
<activity
android:name=".NFCActivity"
android:exported="true" >
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<!--定义了android.nfc.action.TECH_DISCOVERED的具体NFC技术-->
<!--资源文件位于xml/nfc_tech_filter.xml中-->
<meta-data android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/nfc_tech_filter"/>
</activity>
html
<!--xml/nfc_tech_filter.xml文件内容如下-->
<!--该文件为Android所有能支持的NFC类型-->
<resource>
<tech-list>
<tech>android.nfc.tech.NfcA</tech>
<tech>android.nfc.tech.NfcB</tech>
<tech>android.nfc.tech.NfcF</tech>
<tech>android.nfc.tech.NfcV</tech>
<tech>android.nfc.tech.IsoDep</tech>
<tech>android.nfc.tech.Ndef</tech>
<tech>android.nfc.tech.NdefFormatable</tech>
<tech>android.nfc.tech.MifareClassic</tech>
<tech>android.nfc.tech.MifareUltralight</tech>
</tech-list>
</resource>
3.判断NFC功能是否可用(NfcAdapter)
可使用**NfcAdapter(NFC适配器****)**进行判断,NfcAdapter常用方法如下:
- getDefaultAdapter(静态方法):获取NFC适配器对象;设备无NFC功能时返回null。
- isEnabled:判断NFC功能是否可用(即是否开启)。
- enableForegroundDispatch:用于启用NFC感应;第一个参数为上下文环境,第三参数为用于触发的待定意图,第三个参数为过滤器(会触发待定意图的++NFC事件类型++ ),第四个参数为指定++NFC技术类型++的二维数组。
- disableForegroundDispatch:用于禁用NFC感应。
java
//获取NFC适配器
NfcAdapter nfcAdapter=NfcAdapter.getDefaultAdapter(MainActivity.this);
//判断设备是否有NFC功能
if(nfcAdapter==null){
textView.setText("设备无NFC功能");
}
//判断设备是否开启NFC功能
else if (!nfcAdapter.isEnabled()) {
textView.setText("设备未开启NFC功能");
}
else {
textView.setText("设备已开启NFC功能");
}
4.NFC感应(NfcAdapter)启用与禁用
建议在页面暂停时禁用NFC感应,在页面运行时启用NFC感应。
用于触发的待定意图(PendingIntent)为读取NFC信息通常为跳转至当前界面(即会触发++onNewIntent方法++ ) ;请注意需将Activity设置为仅有一个,可在清单文件将launchMode设置为singleTop,或为Intent使用.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)。
java
//准备NFC感应启动参数
//用于触发的待定意图
Intent intent=new Intent(MainActivity.this,MainActivity.class);
//intent=intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent=PendingIntent.getActivity(MainActivity.this,12345,intent,PendingIntent.FLAG_UPDATE_CURRENT|PendingIntent.FLAG_IMMUTABLE);
//过滤器数组(会触发待定意图的NFC事件类型)
IntentFilter[] intentFilter=new IntentFilter[]{new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED)};
//指定NFC技术类型的二维数组
String[][] techList=new String[][]{new String[]{NfcA.class.getName()},{IsoDep.class.getName()}};
protected void onResume() {
super.onResume();
if(nfcAdapter!=null&&nfcAdapter.isEnabled()){
//启用NFC感应
nfcAdapter.enableForegroundDispatch(MainActivity.this,pendingIntent,intentFilter,techList);
}
}
protected void onPause() {
super.onPause();
if(nfcAdapter!=null&&nfcAdapter.isEnabled()){
//禁用NFC感应
nfcAdapter.disableForegroundDispatch(MainActivity.this);
}
}
5.NFC数据解码
用于触发的待定意图(PendingIntent)为读取NFC信息通常为跳转至当前界面(即会触发++onNewIntent方法++ ) ;请注意需将Activity设置为仅有一个。
可以通过重写onNewIntent方法,获取NFC数据并解码:
- 使用Intent获取action并判断是否为NFC操作触发;action返回结果可能为null;可无此步
- 使用Intent获取其中包含的Tag型数据;可使用Tag数据获取卡序列号( getId()方法 ) ;tag返回结果可能为null
- 使用Tag数据获取MifareClassic对象
- MifareClassic对象连接到NFC卡
- 根据MifareClassic对象获取卡中数据
- 关闭MifareClassic与卡的连接,无论是否出现异常
MifareClassic类常用方法:
get(静态方法):从Tag对象中获取卡片对象的信息。
connet:连接卡片数据。
close:释放卡片数据。
getType:获取卡片类型。TYPE_CLASSIC表示传统型,TYPE_PLUS表示加强型,TYPE_PRO表示专业型。
getSectorCount:获取扇形区域。
getBlockCount:获取分块个数。
getSize:获取存储大小,单位字节。
java
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
//读取NFC信息
Tag tag=intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if(tag!=null){
//获取序列号
byte[] id_bytes=tag.getId();
String id="";
for(int i=0;i<id_bytes.length;i++){
id=id+id_bytes[i];
}
//创建MifareClassic对象
MifareClassic classic=MifareClassic.get(tag);
try {
//连接卡片
classic.connect();
//获取类型
int typeI=classic.getType();
String type=null;
if(typeI==MifareClassic.TYPE_CLASSIC){
type="传统类型";
}
else if(typeI==MifareClassic.TYPE_PLUS) {
type="增强类型";
}
else if (typeI==MifareClassic.TYPE_PRO) {
type="专业类型";
}
else {
type="未知类型";
}
//获取其他数据
int i1=classic.getSectorCount();//扇形区域
int i2=classic.getBlockCount();//分块个数
int i3=classic.getSize();//内存大小
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
try {
//无论是否发生异常都要释放卡片数据(关闭连接)
classic.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
6.案例代码一览
以下为MainActivity(界面只有一个TextView)的全部代码,请在清单文件声明(静态申请)NFC权限后使用。
java
public class MainActivity extends AppCompatActivity {
private NfcAdapter nfcAdapter=null;
private PendingIntent pendingIntent=null;
private IntentFilter[] intentFilter=null;
private String[][] techList=null;
private TextView textView=null;
@SuppressLint("MissingInflatedId")
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取控件
textView=findViewById(R.id.textView);
//获取NFC适配器
nfcAdapter=NfcAdapter.getDefaultAdapter(MainActivity.this);
//判断设备是否有NFC功能
if(nfcAdapter==null){
textView.setText("设备无NFC功能");
}
//判断设备是否开启NFC功能
else if (!nfcAdapter.isEnabled()) {
textView.setText("设备未开启NFC功能");
}
else {
textView.setText("设备已开启NFC功能");
}
//准备NFC感应启动参数
//用于触发的待定意图
Intent intent=new Intent(MainActivity.this,MainActivity.class);
//intent=intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
pendingIntent=PendingIntent.getActivity(MainActivity.this,12345,intent,PendingIntent.FLAG_UPDATE_CURRENT|PendingIntent.FLAG_IMMUTABLE);
//过滤器(会触发待定意图的NFC事件类型)
intentFilter=new IntentFilter[]{new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED)};
//指定NFC技术类型
techList=new String[][]{new String[]{NfcA.class.getName()},{IsoDep.class.getName()}};
}
protected void onResume() {
super.onResume();
if(nfcAdapter!=null&&nfcAdapter.isEnabled()){
//启用NFC感应
nfcAdapter.enableForegroundDispatch(MainActivity.this,pendingIntent,intentFilter,techList);
}
}
protected void onPause() {
super.onPause();
if(nfcAdapter!=null&&nfcAdapter.isEnabled()){
//禁用NFC感应
nfcAdapter.disableForegroundDispatch(MainActivity.this);
}
}
//该界面触发NFC自动调用该方法
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
String outString="NFC卡无数据";
//读取NFC信息
Tag tag=intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if(tag!=null){
//获取序列号
byte[] id_bytes=tag.getId();
String id="";
for(int i=0;i<id_bytes.length;i++){
id=id+id_bytes[i];
}
outString="序列号为:"+id+"\n";
//创建MifareClassic对象
MifareClassic classic=MifareClassic.get(tag);
try {
//连接卡片
classic.connect();
//获取类型
int typeI=classic.getType();
String type=null;
if(typeI==MifareClassic.TYPE_CLASSIC){
type="传统类型";
}
else if(typeI==MifareClassic.TYPE_PLUS) {
type="增强类型";
}
else if (typeI==MifareClassic.TYPE_PRO) {
type="专业类型";
}
else {
type="未知类型";
}
//获取其他数据
int i1=classic.getSectorCount();//扇形区域
int i2=classic.getBlockCount();//分块个数
int i3=classic.getSize();//内存大小
outString=outString+type+"\n"+"扇形区域:"+i1+"\n分块个数:"+i2+"\n内存大小:"+i3;
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
try {
//无论是否发生异常都要关闭连接
classic.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
textView.setText(outString);
if(outString.equals("NFC卡无数据")){
Handler handler=new Handler(new Handler.Callback() {
public boolean handleMessage(@NonNull Message message) {
textView.setText("设备已开启NFC功能");
return true;
}
});
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
handler.sendEmptyMessage(123);
}
}).start();
}
}
}
tag:NFC,nfc,芯片,磁卡,近距通信