L'utilisation de NFC sous Android

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;
    }
}

 

Je suppose que tu aimes

Origine blog.csdn.net/weixin_38322371/article/details/115345813
conseillé
Classement