• 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

Nfc 开发记录总结

互联网 diligentman 2周前 (11-18) 10次浏览

整个篇幅 上面为代码,最后面为介绍,如果想直接看我遇到过的坑,请直接拉到最后

 

目录

一、先来个NDEF写入代码:主要在Activity中的生命周期中调用

1⃣️Activity 代码 

 2⃣️工具类代码

 3⃣️Manifest中的 activity注册代码

4⃣️TECH_DISCOVERED resource nfc_tech_filter

5⃣️工具

上面为 Nfc 的所有类型的读取 以及NDEF类型的写入,没有其他类型的写入,暂时没有加密,后期会考虑新写一篇来介绍。

二、注意事项

1、Android 如果想要启动一个应用,并且到达指定界面,需要注意NdefRecord添加的先后顺序,因为他会依次执行,type 必须是小写,否则可能过滤失效,跳往启动页

2、IOS 不能主动识别链接之外的NdefRecord(除非主动去获取),所以NdefRecord[]需要添加一个链接, iOS跳往外链去识别(添加到最后)

3、如果想要给 NdefRecord添加Id 只能自己去New  NdefRecord但是又想达成人家代码的效果



一、先来个NDEF写入代码:主要在Activity中的生命周期中调用

1⃣️Activity 代码 

package com.jiao.demo.nfc;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.nfc.tech.MifareClassic;
import android.nfc.tech.MifareUltralight;
import android.nfc.tech.Ndef;
import android.nfc.tech.NfcA;
import android.nfc.tech.NfcB;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.haier.hailicommontlib.mvp.model.utils.LogUtil;
import com.haier.hailicommontlib.mvp.model.utils.ToastUtil;
import com.jiao.demo.nfc.model.NfcUtil;
import com.jiao.demo.nfc.model.StringUtil;

import java.io.IOException;

/**
 * @author: jiaojunfeng
 * @date: 2020/11/6
 * @describe: NFC demo
 */

public class NFCMainActivity extends Activity implements OnClickListener {

    // NFC适配器
    private NfcAdapter nfcAdapter = null;
    // 传达意图
    private PendingIntent pi = null;
    // 滤掉组件无法响应和处理的Intent
    private IntentFilter tagDetected = null;
    // 文本控件
    private TextView promt = null;
    // 是否支持NFC功能的标签
    private boolean isNFC_support = false;
    // 读、写、删按钮控件
    private Button readBtn, writeBtn, deleteBtn;
    private Button btRead;
    private Button btWrite;
    private Button btDelete;
    private TextView tvContent;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_n_f_c_main);

        initView();

        initNFCData();
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (!isNFC_support) {
            // 如果设备不支持NFC或者NFC功能没开启,就return掉
            ToastUtil.showLongToast(this, "NFC功能受限,本设备不支持或未开启");
            return;
        }
        // 开始监听NFC设备是否连接
        startNFC_Listener();

        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(this.getIntent()
                .getAction())) {
            // 处理该intent
            processIntent(this.getIntent());
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (isNFC_support) {
            // 当前Activity如果不在手机的最前端,就停止NFC设备连接的监听
            stopNFC_Listener();
        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        // 当前app正在前端界面运行,这个时候有intent发送过来,那么系统就会调用onNewIntent回调方法,将intent传送过来
        // 我们只需要在这里检验这个intent是否是NFC相关的intent,如果是,就调用处理方法
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
            processIntent(intent);
        }
    }

    @Override
    public void onClick(View v) {
        // 点击读按钮后
        if (v.getId() == R.id.bt_read) {
            processIntent(getIntent());
            // 点击写后写入
        } else if (v.getId() == R.id.bt_write) {
            try {
                String content = NfcUtil.writeNdef(tagFromIntent);
                settext(content);
            } catch (Exception e) {
                settext("错误:" + e.getMessage());
                Log.e("myonclick", "写nfc异常", e);
            }
        } else if (v.getId() == R.id.bt_delete) {
            try {
                delete(tagFromIntent);
            } catch (IOException e) {
                settext("错误:" + e.getMessage());
            } catch (FormatException e) {
                settext("错误:" + e.getMessage());
                Log.e("myonclick", "删除nfc异常", e);
            }
        }
    }


    private void initNFCData() {
        // 初始化设备支持NFC功能
        isNFC_support = true;
        // 得到默认nfc适配器
        nfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
        // 提示信息定义
        String metaInfo = "";
        // 判定设备是否支持NFC或启动NFC
        if (nfcAdapter == null) {
            metaInfo = "设备不支持NFC!";
            Toast.makeText(this, metaInfo, Toast.LENGTH_SHORT).show();
            isNFC_support = false;
        }
        if (!nfcAdapter.isEnabled()) {
            metaInfo = "请在系统设置中先启用NFC功能!";
            Toast.makeText(this, metaInfo, Toast.LENGTH_SHORT).show();
            isNFC_support = false;
        }

        if (isNFC_support) {
            init_NFC();
        } else {
            promt.setTextColor(Color.RED);
            settext(metaInfo);
        }
    }


    private Tag tagFromIntent;

    /**
     * Parses the NDEF Message from the intent and prints to the TextView
     */
    public void processIntent(Intent intent) {
        if (!isNFC_support || intent == null) {
            ToastUtil.showLongToast(this, "不支持NFC 或数据Intent==NULL");
            return;
        }
        // 取出封装在intent中的TAG
        tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        if (tagFromIntent == null) {
            Toast.makeText(this, "TAG为NULL", Toast.LENGTH_SHORT).show();
            return;
        }
        promt.setTextColor(Color.BLUE);
        String metaInfo = "";
        metaInfo += "卡片ID:" + StringUtil.bytesToHexString(tagFromIntent.getId()) + "n";
        Toast.makeText(this, "找到卡片", Toast.LENGTH_SHORT).show();

        // Tech List
        String[] techList = tagFromIntent.getTechList();

        //分析NFC卡的类型: Mifare Classic/UltraLight Info
        String CardType = "nt" + techList.length + "nt";
        for (String s : techList) {
            LogUtil.I(TAG, "" + s);
            if (s.equals(NfcA.class.getName())) {
                // 读取TAG
                NfcA mfc = NfcA.get(tagFromIntent);
                settext(mfc.getTag().toString());
                try {
                    if ("".equals(CardType))
                        CardType = "MifareClassic卡片类型 n 不支持NDEF消息 n";
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else if (s.equals(NfcB.class.getName())) {
                try {
                    NfcB nfcB = NfcB.get(tagFromIntent);
                    nfcB.connect();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                CardType = "身份证";
            } else if (s.equals(MifareUltralight.class.getName())) {
                MifareUltralight mifareUlTag = MifareUltralight
                        .get(tagFromIntent);
                String lightType = "";
                // Type Info
                switch (mifareUlTag.getType()) {
                    case MifareUltralight.TYPE_ULTRALIGHT:
                        lightType = "Ultralight";
                        break;
                    case MifareUltralight.TYPE_ULTRALIGHT_C:
                        lightType = "Ultralight C";
                        break;
                }
                CardType = lightType + "卡片类型n";

                Ndef ndef = Ndef.get(tagFromIntent);
                CardType += "最大数据尺寸:" + ndef.getMaxSize() + "n";

            } else if (s.equals(MifareClassic.class.getName())) {
                try {
                    CardType = NfcUtil.readCard(tagFromIntent, NfcUtil.MIFARECLASSIC_CARD);
                } catch (IOException | FormatException e) {
                    CardType += "MifareClassic 错误" + e.toString();
                    e.printStackTrace();
                }
            } else if (s.equals(IsoDep.class.getName())) {
                try {
                    CardType = NfcUtil.readCard(tagFromIntent, NfcUtil.ISO_DEP_CARD);
                } catch (IOException | FormatException e) {
                    CardType += "IsoDep 错误" + e.toString();
                    e.printStackTrace();
                }
            } else if (s.equals(Ndef.class.getName())) {
                try {
                    CardType = NfcUtil.readCard(intent, tagFromIntent, NfcUtil.NDEF_CARD);

                } catch (IOException | FormatException e) {
                    CardType += "Ndef 错误" + e.toString();
                    e.printStackTrace();
                }

            }
        }
        metaInfo += CardType;
        settext(metaInfo);
    }


    private String TAG = "NFCMAINac";


    // 删除方法
    private void delete(Tag tag) throws IOException, FormatException {
        if (tag != null) {
            //新建一个里面无任何信息的NdefRecord实例
            NdefRecord nullNdefRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA,
                    new byte[]{}, new byte[]{}, new byte[]{});
            NdefRecord[] records = {nullNdefRecord};
            NdefMessage message = new NdefMessage(records);
            // 解析TAG获取到NDEF实例
            Ndef ndef = Ndef.get(tag);
            // 打开连接
            ndef.connect();
            // 写入信息
            ndef.writeNdefMessage(message);
            // 关闭连接
            ndef.close();
            settext("删除数据成功");

        } else {
            settext("设备与nfc卡连接断开,请重新连接...");
        }
    }

    private void startNFC_Listener() {
        // 开始监听NFC设备是否连接,如果连接就发pi意图
        nfcAdapter.enableForegroundDispatch(this, pi,
                new IntentFilter[]{tagDetected}, null);
    }

    private void stopNFC_Listener() {
        // 停止监听NFC设备是否连接
        nfcAdapter.disableForegroundDispatch(this);
    }

    private void init_NFC() {
        // 初始化PendingIntent,当有NFC设备连接上的时候,就交给当前Activity处理
        pi = PendingIntent.getActivity(this, 0, new Intent(this, getClass())
                .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
        // 新建IntentFilter,使用的是第二种的过滤机制
        tagDetected = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
        tagDetected.addCategory("haili.DEFAULT");
    }

    private void initView() {
        btRead = (Button) findViewById(R.id.bt_read);
        btWrite = (Button) findViewById(R.id.bt_write);
        btDelete = (Button) findViewById(R.id.bt_delete);
        tvContent = (TextView) findViewById(R.id.tv_content);
        btDelete = (Button) findViewById(R.id.bt_delete_log);
        // 控件的绑定
        promt = (TextView) findViewById(R.id.tv_content);
        readBtn = (Button) findViewById(R.id.bt_read);
        writeBtn = (Button) findViewById(R.id.bt_write);
        deleteBtn = (Button) findViewById(R.id.bt_delete);
        // 给文本控件赋值初始文本
        promt.setText("等待RFID标签");
        // 监听读、写、删按钮控件
        readBtn.setOnClickListener(this);
        writeBtn.setOnClickListener(this);
        deleteBtn.setOnClickListener(this);
        btDelete.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                promt.setText("");
            }
        });
    }

    public void settext(String message) {
        promt.setText(promt.getText() + " nt" + message);
    }
}

 2⃣️工具类代码

package com.jiao.demo.nfc.model;

import android.content.Intent;
import android.net.Uri;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.nfc.tech.MifareClassic;
import android.nfc.tech.Ndef;
import android.nfc.tech.NfcB;
import android.os.Parcelable;

import com.haier.hailicommontlib.mvp.model.utils.LogUtil;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
 * @author: jjf
 * @date: 2020/11/9
 * @describe:
 */
public class NfcUtil {
    private static String TAG = "NfcUtil";
    public static String NULL_DATA = "ntNdef is null...nt";
    public static String DEVICE_CONNENT_ERROR = "nt设备与nfc卡连接断开,请重新连接...nt";
    public static final int NFCA_CARD = 11;
    public static final int NFCB_CARD = 12;
    public static final int NDEF_CARD = 21;
    public static final int MIFARECLASSIC_CARD = 31;
    public static final int ISO_DEP_CARD = 41;
 

    public static String readCard(Tag tag, int cartType) throws IOException, FormatException {
        switch (cartType) {
            case NFCA_CARD:

            case NFCB_CARD:
                return readNfcB(tag);
            case NDEF_CARD:
                return readNdefRecordPayload(tag);
            case MIFARECLASSIC_CARD:
                return readMifareClassic(tag);
            case ISO_DEP_CARD:
                return IsoDep(tag);
        }
        return "未发现的芯片类型";
    }

    /**
     * 读取Ndef 信息  根据Id 获取指定NdefRecord 的附加参数
     * @param tag
     * @param id  指定NdefRecord (消息录入时 设置的Id)
     * @return
     * @throws IOException
     * @throws FormatException
     */
    public static String readNdef(Tag tag, String id) throws IOException, FormatException {

        //解析Tag获取到NDEF实例


        Ndef ndef = Ndef.get(tag);
        if (ndef == null) {
            return NULL_DATA;
        }
        //打开连接
        if (!ndef.isConnected()) {
            ndef.connect();
        }

        //获取NDEF消息
        NdefMessage message = ndef.getNdefMessage();
        if (message == null) {
            ndef.close();
            return "NdefMessage==null0";
        }
        NdefRecord[] ndefRecord = message.getRecords();
        for (NdefRecord ndefRecord1 : ndefRecord) {
            byte[] payload = ndefRecord1.getPayload();
            if (payload != null) {
                String str = new String(payload, StandardCharsets.UTF_8);
                System.out.println(" " + str);
            }
        }
        //将消息转换成字节数组
        byte[] data = message.toByteArray();
        //将字节数组转换成字符串
        String str = new String(data, StandardCharsets.UTF_8);
        //关闭连接
        ndef.close();
        System.out.println(" " + str);
        return str;

    }

    // 读取Ndef 参数,所有NdefRecord 附带参数拼接一起返回
    public static String readNdefRecordPayload(Tag tag) {
        StringBuilder str = new StringBuilder();
        Ndef ndef = null;
        try {
            //解析Tag获取到NDEF实例

            if (tag == null) {
                return "Tag==null";
            }
            ndef = Ndef.get(tag);
            if (ndef == null) {
                return NfcUtil.NULL_DATA;
            }
            //打开连接
            ndef.connect();
            //获取NDEF消息
            NdefMessage message = ndef.getNdefMessage();
            if (message == null) {
                ndef.close();
                return "NdefMessage==null";
            }
            NdefRecord[] ndefRecord = message.getRecords();

            for (NdefRecord ndefRecord1 : ndefRecord) {
                byte[] payload = ndefRecord1.getPayload();
                if (payload != null) {
                    if (str.toString().length() != 0) {
                        str.append("&");
                    }
                    str.append(new String(payload, StandardCharsets.UTF_8));

                }
            }
            //关闭连接
            ndef.close();
            return str.toString();

        } catch (Exception e) {
            LogUtil.I("Base", "NfcUtil  " + e.toString());
            e.printStackTrace();
        } finally {
            if (ndef != null) {
                try {
                    ndef.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return str.toString();
    }

    // 读取NfcB
    public static String readNfcB(Tag tag) throws IOException, FormatException {
        if (tag != null) {
            //解析Tag获取到NDEF实例
            NfcB ndef = NfcB.get(tag);
            if (ndef == null) {
                return NULL_DATA;
            }
            try {
                //打开连接
                ndef.connect();
            } catch (Exception e) {
                e.printStackTrace();
            }
            //获取NfcB消息
            byte[] message = ndef.getProtocolInfo();

            for (int i = 0; message != null && i < message.length; i++) {
                LogUtil.I(TAG, "message[" + i + "]" + message[i]);
            }
            //将字节数组转换成字符串
            String str = new String(message, StandardCharsets.UTF_8);
            //关闭连接
            ndef.close();
            return str;
        } else {
            return DEVICE_CONNENT_ERROR;
        }
    }

    // Ndef 消息写入方法
    public static String writeNdef(Tag tag) throws Exception {
        if (tag != null) {
            //新建NdefRecord数组,本例中数组只有一个元素
            NdefRecord[] records = createNdefRecordByNFCUri("deviceId=123456");
            //新建一个NdefMessage实例
            NdefMessage message = new NdefMessage(records);
            // 解析TAG获取到NDEF实例
            Ndef ndef = Ndef.get(tag);
            // 打开连接
            if (!ndef.isConnected()) {
                ndef.connect();
            }

            // 写入NDEF信息
            ndef.writeNdefMessage(message);
            // 关闭连接
            ndef.close();
            return "写入数据成功!";
        } else {
            return DEVICE_CONNENT_ERROR;
        }
    }

    // Ndef简单文本写入
    public static NdefRecord createRecord() {
        //组装字符串,准备好你要写入的信息
        String msg = "BEGIN:HAHAn" + "VERSION:1.0n" + "DEVICE_ID:123456n"
                + "hl_companyn" + "END:HAHA";
        //将字符串转换成字节数组
        byte[] textBytes = msg.getBytes();
        //将字节数组封装到一个NdefRecord实例中去
        NdefRecord textRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA,
                "text/*".getBytes(), new byte[]{}, textBytes);
        return textRecord;
    }


    /**
     * Ndef 创建消息(录入NFC)
     *
     * @param content
     * @return
     */
    public static NdefRecord[] createNdefRecordByNFCUri(String content) {
        //消息ID
        String id = "hailiWasher01";
        //这一段是对应的想要打开的Activity中添加的意图过滤参数pathPrefix 为自定义(前面要有斜杠),其余为固定写法
        //<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.jiao.demo.nfc.nfcmainactivity:nfc"/>
        //</intent-filter>
        return new NdefRecord[]{
                //打开App , ,第一个参数跟第二个参数就是Manifest中的android:pathPrefix,第三个参数是自己添加的参数
                createExternal("com.jiao.demo.nfc.nfcmainactivity", id, "nfc", content.getBytes()),
                //想要打开的应用包名(如果是组件化项目,包名必须是主项目的包名,也就是说,模块的build.gradle 中顶部代码为 apply plugin: 'com.android.application')
                //如果没有安装这个应用,则会跳往应用市场
                NdefRecord.createApplicationRecord("com.haier.haierwashertopspeed"),
                //打开的链接,这里主要用于IOS 识别
                NdefRecord.createUri("https://www.baidu.com" + "?" + content),
        };
    }

    /**
     * 重写NdefRecord方法 添加Id
     * {@link NdefRecord#createExternal(String, String, byte[])}
     *
     * @param domain domain-name of issuing organization (自定义的,Activity路径 与 @param type   一起组成参数pathPrefix)
     * @param type   domain-specific type of data (自定义的,Activity路径 与 @param domain   一起组成参数pathPrefix)
     * @param id     消息Id
     * @param data   自添加数据 可以用来追加参数
     * @return
     */
    public static NdefRecord createExternal(String domain, String id, String type, byte[] data) {
        if (domain == null) throw new NullPointerException("domain is null");
        if (type == null) throw new NullPointerException("type is null");

        domain = domain.trim().toLowerCase(Locale.ROOT);
        type = type.trim().toLowerCase(Locale.ROOT);

        if (domain.length() == 0) throw new IllegalArgumentException("domain is empty");
        if (type.length() == 0) throw new IllegalArgumentException("type is empty");

        byte[] byteDomain = domain.getBytes(StandardCharsets.UTF_8);
        byte[] byteType = type.getBytes(StandardCharsets.UTF_8);
        byte[] b = new byte[byteDomain.length + 1 + byteType.length];
        System.arraycopy(byteDomain, 0, b, 0, byteDomain.length);
        b[byteDomain.length] = ':';
        System.arraycopy(byteType, 0, b, byteDomain.length + 1, byteType.length);

        return new NdefRecord(NdefRecord.TNF_EXTERNAL_TYPE, b, id == null ? null : id.getBytes(), data);
    }


    //读取EXTRA_NDEF_MESSAGES内容:
    public static String readFromTag(Intent intent) {
        Parcelable[] rawArray = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);

        NdefMessage mNdefMsg = (NdefMessage) rawArray[0];
        NdefRecord mNdefRecord = mNdefMsg.getRecords()[0];
        try {
            if (mNdefRecord != null) {
                return new String(mNdefRecord.getPayload(), "UTF-8");
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 读取MifareClassic 类型
     *
     * @param tag
     * @return
     */
    public static String readMifareClassic(Tag tag) {
        //取出封装在intent中的TAG

        String CardId = StringUtil.bytesToHexString(tag.getId());
        String metaInfo = "";
        metaInfo += "卡片ID:" + CardId;
        for (String tech : tag.getTechList()) {
            System.out.println(tech);
        }
        boolean auth;
        //读取TAG
        MifareClassic mfc = MifareClassic.get(tag);
        try {
            //Enable I/O operations to the tag from this TagTechnology object.
            if (!mfc.isConnected()) {
                mfc.connect();
            }
            int type = mfc.getType();//获取TAG的类型
            int sectorCount = mfc.getSectorCount();//获取TAG中包含的扇区数
            String typeS = "";
            switch (type) {
                case MifareClassic.TYPE_CLASSIC:
                    typeS = "TYPE_CLASSIC";
                    break;
                case MifareClassic.TYPE_PLUS:
                    typeS = "TYPE_PLUS";
                    break;
                case MifareClassic.TYPE_PRO:
                    typeS = "TYPE_PRO";
                    break;
                case MifareClassic.TYPE_UNKNOWN:
                    typeS = "TYPE_UNKNOWN";
                    break;
            }
            metaInfo += "n卡片类型:" + typeS + "n共" + sectorCount + "个扇区n共"
                    + mfc.getBlockCount() + "个块n存储空间: " + mfc.getSize() + "Bn";
            for (int j = 0; j < sectorCount; j++) {
                //Authenticate a sector with key A.
                auth = mfc.authenticateSectorWithKeyA(j,
                        MifareClassic.KEY_DEFAULT);
                int bCount;
                int bIndex;
                if (auth) {
                    metaInfo += "Sector " + j + ":验证成功n";
                    // 读取扇区中的块
                    bCount = mfc.getBlockCountInSector(j);
                    bIndex = mfc.sectorToBlock(j);
                    for (int i = 0; i < bCount; i++) {
                        byte[] data = mfc.readBlock(bIndex);
                        metaInfo += "Block " + bIndex + " : "
                                + StringUtil.bytesToHexString(data) + "n";
                        bIndex++;
                    }
                } else {
                    metaInfo += "Sector " + j + ":验证失败n";
                }
            }
            return metaInfo;
            //Toast.makeText(this, metaInfo, Toast.LENGTH_SHORT).show();
        } catch (Exception e) {
            e.printStackTrace();
            return "读取错误:" + e.toString();
        }

    }

    /**
     * 读取IsoDep
     *
     * @param tag
     * @return
     */
    public static String IsoDep(Tag tag) {
        IsoDep isoDep = IsoDep.get(tag);
        String str = "";
        try {
            isoDep.connect(); // 连接
            if (isoDep.isConnected()) {
                LogUtil.D(TAG, "isoDep.isConnected"); // 判断是否连接上
                // 1.select PSF (1PAY.SYS.DDF01)
                // 选择支付系统文件,它的名字是1PAY.SYS.DDF01。
                byte[] DFN_PSE = {(byte) '1', (byte) 'P', (byte) 'A', (byte) 'Y', (byte) '.', (byte) 'S', (byte) 'Y', (byte) 'S', (byte) '.', (byte) 'D', (byte) 'D', (byte) 'F', (byte) '0', (byte) '1',};
                byte[] payFile = isoDep.transceive(getSelectCommand(DFN_PSE));
                String EN_CODE_TYPE = "GBK";
                String payFileStr = new String(payFile, EN_CODE_TYPE);
                str += "nt" + "支付系统:" + payFileStr;

                // 2.选择公交卡应用的名称
                byte[] DFN_SRV = {(byte) 0xA0, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x86, (byte) 0x98, (byte) 0x07, (byte) 0x01,};
                byte[] card_name = isoDep.transceive(getSelectCommand(DFN_SRV));
                String card_nameStr = new String(card_name, EN_CODE_TYPE);
                str += "nt" + "公交卡应用的名称:" + card_nameStr;
                // 3.读取余额
                byte[] ReadMoney = {(byte) 0x80, // CLA Class
                        (byte) 0x5C, // INS Instruction
                        (byte) 0x00, // P1 Parameter 1
                        (byte) 0x02, // P2 Parameter 2
                        (byte) 0x04, // Le
                };
                byte[] Money = isoDep.transceive(ReadMoney);
                String MoneyStr = new String(card_name, EN_CODE_TYPE);
                str += "nt" + "余额1:" + MoneyStr;
                if (Money != null && Money.length > 4) {
                    int cash = byteToInt(Money, 4);
                    float ba = cash / 100.0f;

                    str += "nt" + "余额2:" + ba;
                }
                // 4.读取所有交易记录
                byte[] ReadRecord = {(byte) 0x00, // CLA Class
                        (byte) 0xB2, // INS Instruction
                        (byte) 0x01, // P1 Parameter 1
                        (byte) 0xC5, // P2 Parameter 2
                        (byte) 0x00, // Le
                };
                byte[] Records = isoDep.transceive(ReadRecord);
                if (Records != null && Records.length > 4) {
                    int cash = byteToInt(Records, 4);
                    float ba = cash / 100.0f;

                    str += "nt" + "总消费记录:" + ba;
                }


                ArrayList<byte[]> ret = parseRecords(Records);
                List<String> retList = parseRecordsToStrings(ret);

                str = str + "nt" + "消费记录" + retList.size() + "条";
                if (retList.size() > 0) {
                    str = str + ",如下:";
                }
                for (String string : retList) {
                    LogUtil.D(TAG, "消费记录" + string);
                    str = str + "nt" + string;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (isoDep != null) {
                try {
                    isoDep.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
        return str;
    }


    public static byte byteToHex(byte arg) {
        byte hex = 0;
        if (arg >= 48 && arg <= 57) {
            hex = (byte) (arg - 48);
        } else if (arg >= 65 && arg <= 70) {
            hex = (byte) (arg - 55);
        } else if (arg >= 97 && arg <= 102) {
            hex = (byte) (arg - 87);
        }
        return hex;
    }

    private static byte[] getSelectCommand(byte[] aid) {
        final ByteBuffer cmd_pse = ByteBuffer.allocate(aid.length + 6);
        cmd_pse.put((byte) 0x00) // CLA Class
                .put((byte) 0xA4) // INS Instruction
                .put((byte) 0x04) // P1 Parameter 1
                .put((byte) 0x00) // P2 Parameter 2
                .put((byte) aid.length) // Lc
                .put(aid).put((byte) 0x00); // Le
        return cmd_pse.array();
    }


    // byteArray转化为int
    private static int byteToInt(byte[] b, int n) {
        int ret = 0;
        for (int i = 0; i < n; i++) {
            ret = ret << 8;
            ret |= b[i] & 0x00FF;
        }
        if (ret > 100000 || ret < -100000)
            ret -= 0x80000000;
        return ret;
    }

    /**
     * 整条Records解析成ArrayList<byte[]>
     *
     * @param Records
     * @return
     */
    private static ArrayList<byte[]> parseRecords(byte[] Records) {
        int max = Records.length / 23;
        LogUtil.D(TAG, "消费记录有" + max + "条");
        ArrayList<byte[]> ret = new ArrayList<byte[]>();
        for (int i = 0; i < max; i++) {
            byte[] aRecord = new byte[23];
            for (int j = 23 * i, k = 0; j < 23 * (i + 1); j++, k++) {
                aRecord[k] = Records[j];
            }
            ret.add(aRecord);
        }
        for (byte[] bs : ret) {
            LogUtil.D(TAG, "消费记录有byte[]" + bs); // 有数据。解析正确。
        }
        return ret;
    }

    /**
     * ArrayList<byte[]>记录分析List<String> 一条记录是23个字节byte[] data,对其解码如下
     * data[0]-data[1]:index data[2]-data[4]:over,金额溢出??? data[5]-data[8]:交易金额
     * ??代码应该是(5,4) data[9]:如果等于0x06或者0x09,表示刷卡;否则是充值
     * data[10]-data[15]:刷卡机或充值机编号
     * data[16]-data[22]:日期String.format("%02X%02X.%02X.%02X %02X:%02X:%02X"
     * ,data[16], data[17], data[18], data[19], data[20], data[21], data[22]);
     *
     * @return
     */
    private static List<String> parseRecordsToStrings(ArrayList<byte[]>... Records) {
        List<String> recordsList = new ArrayList<String>();
        for (ArrayList<byte[]> record : Records) {
            if (record == null)
                continue;
            for (byte[] v : record) {
                StringBuilder r = new StringBuilder();
                int cash = NumberUtil.toInt(v);
                char t = (v[9] == TRANS_CSU || v[9] == TRANS_CSU_CPX) ? '-' : '+';
                r.append(String.format("%02X%02X.%02X.%02X %02X:%02X ", v[16], v[17], v[18], v[19], v[20], v[21], v[22]));
                r.append("   " + t).append(cash / 100.0f);
                String aLog = r.toString();
                recordsList.add(aLog);
            }
        }
        return recordsList;
    }

    protected final static byte TRANS_CSU = 6; // 如果等于0x06或者0x09,表示刷卡;否则是充值
    protected final static byte TRANS_CSU_CPX = 9; // 如果等于0x06或者0x09,表示刷卡;否则是充值
}

 3⃣️Manifest中的 activity注册代码

  <activity
            android:name="com.jiao.demo.nfc.NFCMainActivity"
            android:configChanges="keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout|fontScale|touchscreen"
            android:launchMode="singleInstance"
            android:maxAspectRatio="2.4"
            android:label="NFC参数修改"
            android:screenOrientation="portrait">

            <!--   NFC  start-->
            <!-- NFC 过滤目标 -->
            <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.jiao.demo.nfc.nfcmainactivity:nfc"/>
            </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" />
            <!--   NFC  start-->
        </activity>

4⃣️TECH_DISCOVERED resource nfc_tech_filter

 下面是nfc_tech_filter 中的代码, 这个文件的位置为src/main/res/xml/nfc_tech_filter.xml

因为我测试的Nfc 只有三个类型,所以我只写这几个,这也是一层对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>-->
    <tech-list>
        <tech>android.nfc.tech.MifareUltralight</tech>
        <tech>android.nfc.tech.NfcA</tech>
        <tech>android.nfc.tech.Ndef</tech>
    </tech-list>
</resources>

5⃣️工具类

public class NumberUtil {
    public static int toInt(byte[] bytes){
        int number = 0;
        for(int i = 0; i < 4 ; i++){
            number += bytes[i] << i*8;
        }
        return number;
    }
}


public class StringUtil {

    // 字符序列转换为16进制字符串
    public static String bytesToHexString(byte[] src) {
        return bytesToHexString(src, true);
    }

    public static String bytesToHexString(byte[] src, boolean isPrefix) {
        StringBuilder stringBuilder = new StringBuilder();
        if (isPrefix) {
            stringBuilder.append("0x");
        }
        if (src == null || src.length <= 0) {
            return null;
        }
        char[] buffer = new char[2];
        for (int i = 0; i < src.length; i++) {
            buffer[0] = Character.toUpperCase(Character.forDigit(
                    (src[i] >>> 4) & 0x0F, 16));
            buffer[1] = Character.toUpperCase(Character.forDigit(src[i] & 0x0F,
                    16));
            System.out.println(buffer);
            stringBuilder.append(buffer);
        }
        return stringBuilder.toString();
    }


}

 

上面为 Nfc 的所有类型的读取 以及NDEF类型的写入,没有其他类型的写入,暂时没有加密,后期会考虑新写一篇来介绍。

二、注意事项

1、Android 如果想要启动一个应用,并且到达指定界面,需要注意NdefRecord添加的先后顺序,因为他会依次执行,type 必须是小写,否则可能过滤失效,跳往启动页

2、IOS 不能主动识别链接之外的NdefRecord(除非主动去获取),所以NdefRecord[]需要添加一个链接, iOS跳往外链去识别(添加到最后)

//创建消息(录入NFC)
public static NdefRecord[] createNdefRecordByNFCUri(String content) {
    //这一段是对应的想要打开的Activity中添加的意图过滤参数pathPrefix 为自定义(前面要有斜杠),其余为固定写法
    //<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.jiao.demo.nfc.nfcmainactivity:nfc"/>
    //</intent-filter>
    //
    return new NdefRecord[]{
            //打开App , ,第一个参数跟第二个参数就是Manifest中的android:pathPrefix,第三个参数是自己添加的参数
            NdefRecord.createExternal("com.jiao.demo.nfc.nfcmainactivity", "nfc", content.getBytes()),
            //想要打开的应用包名(如果是组件化项目,包名必须是主项目的包名,也就是说,模块的build.gradle 中顶部代码为 apply plugin: 'com.android.application')
            //如果没有安装这个应用,则会跳往应用市场
            NdefRecord.createApplicationRecord("com.*.*"),
            //打开的链接,这里主要用于IOS 识别
            NdefRecord.createUri("https://www.baidu.com" + "?" + content),
    };
}

3、如果想要给 NdefRecord添加Id 只能自己去New  NdefRecord但是又想达成人家代码的效果,那就复制一下源码,给加个ID 比如

Nfc 开发记录总结Nfc 开发记录总结

至此,待补充。。。


喜欢 (0)