Le développement NFC est également quelque chose que nous rencontrerons dans le développement, nous devons donc également comprendre.
1. Configuration des autorisations
<!-- NFC -->
<uses-permission android:name="android.permission.NFC" />
<!-- 仅在支持NFC的设备上运行 -->
<uses-feature android:name="android.hardware.nfc" android:required="true" />
2. Configuration associée à AndroidManifest.xml
Configurez les éléments suivants pour l'activité correspondante
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
</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" />
</intent-filter>
<meta-data
android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/nfc_tech_filter" />
</activity>
Parmi eux, le fichier nfc_tech_filter.xml est le suivant
<resources>
<!-- 可以处理所有Android支持的NFC类型 -->
<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>
</resources>
3. Différents scénarios d'utilisation NFC
Nom du format de données NFC | Nom de la norme ISO | Application réelle |
NfcA | ISO 14443-3A | Carte d'accès |
NfcB | ISO 14443-3B | Carte d'identité de deuxième génération |
NfcF | JIS 6319-4 | Poulpe |
NfcV | ISO 15693 | Carte de lecteur de bibliothèque de Shenzhen |
IsoDep | ISO 14443-4 | Pékin Pass, Shenzhen Pass, Xi'an Chang'an Pass, Wuhan Pass, Guangzhou Yangcheng Pass |
4. Initialisez NFC
Déclarer un objet adaptateur NFC
private NfcAdapter mNfcAdapter;
Vient ensuite la méthode d'initialisation
// 1.初始化NFC适配器
private void initNfc() {
// 获取系统默认的NFC适配器
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (mNfcAdapter == null) {
tv_nfc_result.setText("当前手机不支持NFC");
} else if (!mNfcAdapter.isEnabled()) {
tv_nfc_result.setText("请先在系统设置中启用NFC功能");
} else {
tv_nfc_result.setText("当前手机支持NFC");
}
}
Appelez ensuite la méthode initNfc dans la méthode onCreate
initNfc();
En même temps, nous définissons une zone de texte sur la mise en page et l'initialisons
5. Activez la détection NFC, désactivez la détection NFC
@Override
protected void onResume() {
super.onResume();
if (mNfcAdapter==null || !mNfcAdapter.isEnabled()) {
return;
}
// 探测到NFC卡片后,必须以FLAG_ACTIVITY_SINGLE_TOP方式启动Activity,
// 或者在AndroidManifest.xml中设置launchMode属性为singleTop或者singleTask,
// 保证无论NFC标签靠近手机多少次,Activity实例都只有一个。
Intent intent = new Intent(this, MainActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
// 声明一个NFC卡片探测事件的相应动作
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
// 读标签之前先确定标签类型。这里以大多数的NfcA为例
String[][] techLists = new String[][]{new String[]{NfcA.class.getName()}, {IsoDep.class.getName()}};
try {
// 定义一个过滤器(检测到NFC卡片)
IntentFilter[] filters = new IntentFilter[]{new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED, "*/*")};
// 为本App启用NFC感应
mNfcAdapter.enableForegroundDispatch(this, pendingIntent, filters, techLists);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onPause() {
super.onPause();
if (mNfcAdapter==null || !mNfcAdapter.isEnabled()) {
return;
}
// 禁用本App的NFC感应
mNfcAdapter.disableForegroundDispatch(this);
}
6. Après avoir reçu le message d'induction et décodé le message
Après avoir activé la détection NFC à l'étape 5 précédente, une fois que l'APP reçoit le message de détection, elle rappelle la fonction onNewIntent de l'activité, afin que le développeur puisse réécrire cette fonction pour traiter le contenu du message NFC. Les données de la carte peuvent être obtenues en utilisant les méthodes associées de la classe MifareClassic. Voici la description de la méthode de la classe MifareClassic.
- get: récupère les informations sur l'objet carte à partir de l'objet Tag. Cette méthode est statique.
- connect: Liez les données de la carte
- close: Libère les données de la carte.
- getType: récupère le type de carte. TYPE_CLASSIC représente le type traditionnel, TYPE_PLUS représente le type amélioré et TYPE_PRO représente le type professionnel.
- getSectorCount: Obtenez le nombre de secteurs de la carte.
- getBlockCount: Obtenez le nombre de blocs de la carte.
- getSize: Obtenez la taille de l'espace de stockage de la carte, en octets.
Le code spécifique est le suivant
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
String action = intent.getAction(); // 获取到本次启动的action
if (action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED) // NDEF类型
|| action.equals(NfcAdapter.ACTION_TECH_DISCOVERED) // 其他类型
|| action.equals(NfcAdapter.ACTION_TAG_DISCOVERED)) { // 未知类型
// 从intent中读取NFC卡片内容
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
// 获取NFC卡片的序列号
byte[] ids = tag.getId();
String card_info = String.format("卡片的序列号为: %s",
ByteArrayToHexString(ids));
String result = readGuardCard(tag);
card_info = String.format("%s\n详细信息如下:\n%s", card_info, result);
tv_nfc_result.setText(card_info);
}
}
// 读取小区门禁卡信息
public String readGuardCard(Tag tag) {
MifareClassic classic = MifareClassic.get(tag);
for (String tech : tag.getTechList()) {
Log.d(TAG, "当前设备支持" + tech); //显示设备支持技术
}
String info;
try {
classic.connect(); // 连接卡片数据
int type = classic.getType(); //获取TAG的类型
String typeDesc;
if (type == MifareClassic.TYPE_CLASSIC) {
typeDesc = "传统类型";
} else if (type == MifareClassic.TYPE_PLUS) {
typeDesc = "增强类型";
} else if (type == MifareClassic.TYPE_PRO) {
typeDesc = "专业类型";
} else {
typeDesc = "未知类型";
}
info = String.format("\t卡片类型:%s\n\t扇区数量:%d\n\t分块个数:%d\n\t存储空间:%d字节",
typeDesc, classic.getSectorCount(), classic.getBlockCount(), classic.getSize());
} catch (Exception e) {
e.printStackTrace();
info = e.getMessage();
} finally { // 无论是否发生异常,都要释放资源
try {
classic.close(); // 释放卡片数据
} catch (Exception e) {
e.printStackTrace();
info = e.getMessage();
}
}
return info;
}
public static String ByteArrayToHexString(byte[] bytesId) {
int i, j, in;
String[] hex = {
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"
};
String output = "";
for (j = 0; j < bytesId.length; ++j) {
in = bytesId[j] & 0xff;
i = (in >> 4) & 0x0f;
output += hex[i];
i = in & 0x0f;
output += hex[i];
}
return output;
}
Nous pouvons donc lire le contenu correspondant de la carte.
Le code complet est le suivant:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_nfc_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="17sp" />
</LinearLayout>
MainActivity.java
public class MainActivity extends WaterPermissionActivity {
private NfcAdapter mNfcAdapter; // 声明一个NFC适配器对象
private TextView tv_nfc_result;
@Override
protected MvcBaseModel getModelImp() {
return null;
}
@Override
protected int getContentLayoutId() {
return R.layout.activity_main;
}
@Override
protected void initWidget() {
tv_nfc_result = findViewById(R.id.tv_nfc_result);
initNfc();
}
// 1.初始化NFC适配器
private void initNfc() {
// 获取系统默认的NFC适配器
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (mNfcAdapter == null) {
tv_nfc_result.setText("当前手机不支持NFC");
} else if (!mNfcAdapter.isEnabled()) {
tv_nfc_result.setText("请先在系统设置中启用NFC功能");
} else {
tv_nfc_result.setText("当前手机支持NFC");
}
}
//2.
@Override
protected void onResume() {
super.onResume();
if (mNfcAdapter==null || !mNfcAdapter.isEnabled()) {
return;
}
// 探测到NFC卡片后,必须以FLAG_ACTIVITY_SINGLE_TOP方式启动Activity,
// 或者在AndroidManifest.xml中设置launchMode属性为singleTop或者singleTask,
// 保证无论NFC标签靠近手机多少次,Activity实例都只有一个。
Intent intent = new Intent(this, MainActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
// 声明一个NFC卡片探测事件的相应动作
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
// 读标签之前先确定标签类型。这里以大多数的NfcA为例
String[][] techLists = new String[][]{new String[]{NfcA.class.getName()}, {IsoDep.class.getName()}};
try {
// 定义一个过滤器(检测到NFC卡片)
IntentFilter[] filters = new IntentFilter[]{new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED, "*/*")};
// 为本App启用NFC感应
mNfcAdapter.enableForegroundDispatch(this, pendingIntent, filters, techLists);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onPause() {
super.onPause();
if (mNfcAdapter==null || !mNfcAdapter.isEnabled()) {
return;
}
// 禁用本App的NFC感应
mNfcAdapter.disableForegroundDispatch(this);
}
//3.
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
String action = intent.getAction(); // 获取到本次启动的action
if (action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED) // NDEF类型
|| action.equals(NfcAdapter.ACTION_TECH_DISCOVERED) // 其他类型
|| action.equals(NfcAdapter.ACTION_TAG_DISCOVERED)) { // 未知类型
// 从intent中读取NFC卡片内容
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
// 获取NFC卡片的序列号
byte[] ids = tag.getId();
String card_info = String.format("卡片的序列号为: %s",
ByteArrayToHexString(ids));
String result = readGuardCard(tag);
card_info = String.format("%s\n详细信息如下:\n%s", card_info, result);
tv_nfc_result.setText(card_info);
}
}
// 读取小区门禁卡信息
public String readGuardCard(Tag tag) {
MifareClassic classic = MifareClassic.get(tag);
for (String tech : tag.getTechList()) {
Log.d(TAG, "当前设备支持" + tech); //显示设备支持技术
}
String info;
try {
classic.connect(); // 连接卡片数据
int type = classic.getType(); //获取TAG的类型
String typeDesc;
if (type == MifareClassic.TYPE_CLASSIC) {
typeDesc = "传统类型";
} else if (type == MifareClassic.TYPE_PLUS) {
typeDesc = "增强类型";
} else if (type == MifareClassic.TYPE_PRO) {
typeDesc = "专业类型";
} else {
typeDesc = "未知类型";
}
info = String.format("\t卡片类型:%s\n\t扇区数量:%d\n\t分块个数:%d\n\t存储空间:%d字节",
typeDesc, classic.getSectorCount(), classic.getBlockCount(), classic.getSize());
} catch (Exception e) {
e.printStackTrace();
info = e.getMessage();
} finally { // 无论是否发生异常,都要释放资源
try {
classic.close(); // 释放卡片数据
} catch (Exception e) {
e.printStackTrace();
info = e.getMessage();
}
}
return info;
}
public static String ByteArrayToHexString(byte[] bytesId) {
int i, j, in;
String[] hex = {
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"
};
String output = "";
for (j = 0; j < bytesId.length; ++j) {
in = bytesId[j] & 0xff;
i = (in >> 4) & 0x0f;
output += hex[i];
i = in & 0x0f;
output += hex[i];
}
return output;
}
}