美文网首页
Android-NFC

Android-NFC

作者: 前山饭店 | 来源:发表于2018-03-20 21:47 被阅读213次

本篇博客是为了解决以下的问题:

  1. NFC是什么
  2. NFC的原理
  3. NFC工作模式和实用场景
  4. NFC相比于蓝牙的优势
  5. Android读取和写入NDEF格式的NFC芯片
  6. Android读取和写入非NDEF格式的NFC芯片

NFC是什么

NFC是Near Field Communication的简称,意思为近距离无线通讯技术,简单的说是能够在短距离之内与兼容设备实现数据交换的技术。

NFC的原理

NFC是使用非接触式射频技术实现的数据交换技术,在13.56MHz频率20厘米距离内进行传输,其传输速度有106 Kbit/秒、212 Kbit/秒或者424 Kbit/秒三种。

NFC工作模式和实用场景

NFC工作模式分为三种:

  • 读卡器模式,指含有NFC芯片的标签,可以被支持NFC的手机或者专门读卡器设备读取或者写入数据。一般可以用于巡更,用支持NFC的手机到定点区读取NFC芯片的数据然后传输到后台表示已巡视了定点区。
  • 仿真卡模式,指含有IC卡的NFC芯片,可以使用支持NFC的手机或者NFC射频器读取出NFC芯片中IC卡的信息。常见的由交通卡,信用卡。
  • 点对点模式,指两台NFC设备之间互相都可以进行数据 交换。

NFC相比于蓝牙的优势

蓝牙4.0相比于以前版本,功耗低,传输速度快,可以在10m之内使用。
NFC技术优势是创建连接快,传输速度也不慢,存储芯片中的数据可以加密安全性较高,在20cm之内使用。

Android读取和写入NDEF格式的NFC芯片

首先说明NFC芯片可以根据厂家定制,一次性写入然后只能读,简单的可读可写。目前Android SDK API主要支持NFC论坛标准(Forum Standard),这种标准被称为NDEF。

1.支持NFC功能,添加权限
<uses-permission android:name="android.permission.NFC" />
<!-- 要求当前设备必须要有NFC芯片 -->
<uses-feature android:name="android.hardware.nfc" android:required="true" />
2.当前Activity的清单文件中的设置
<activity
    android:name="com.example.NFCActivity"
    android:launchMode="singleTask" >
    <intent-filter>    
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
3.当前Activity声明周期的判断
private NfcAdapter mNfcAdapter;
private PendingIntent mPendingIntent;

@Override
    protected void onStart() {
        super.onStart();
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        //一旦截获NFC消息,就会通过PendingIntent调用窗口
        mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()), 0);
    }
    /**
     * 获得焦点,按钮可以点击
     */
    @Override
    public void onResume() {
        super.onResume();
        //后面两个null,null设置处理优于所有其他NFC的处理
        if (mNfcAdapter != null)
            mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
    }
    /**
     * 暂停Activity,界面获取焦点,按钮可以点击
     */
    @Override
    public void onPause() {
        super.onPause();
        //恢复默认状态
        if (mNfcAdapter != null)
            mNfcAdapter.disableForegroundDispatch(this);
    }
4.读取NFC芯片中内容
@Override
    public void onNewIntent(Intent intent) {
        //1.获取Tag对象
        Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        //2.获取Ndef的实例
        Ndef ndef = Ndef.get(detectedTag);
        mTagText = ndef.getType() + "\nmaxsize:" + ndef.getMaxSize() + "bytes\n\n";
        readNfcTag(intent);
        mNfcText.setText(mTagText);
    }
    /**
     * 读取NFC标签文本数据
     */
    private void readNfcTag(Intent intent) {
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
            Parcelable[] rawMsgs = intent.getParcelableArrayExtra(
                    NfcAdapter.EXTRA_NDEF_MESSAGES);
            NdefMessage msgs[] = null;
            int contentSize = 0;
            if (rawMsgs != null) {
                msgs = new NdefMessage[rawMsgs.length];
                for (int i = 0; i < rawMsgs.length; i++) {
                    msgs[i] = (NdefMessage) rawMsgs[i];
                    contentSize += msgs[i].toByteArray().length;
                }
            }
            try {
                if (msgs != null) {
                    NdefRecord record = msgs[0].getRecords()[0];
                    String textRecord = parseTextRecord(record);
                    mTagText += textRecord + "\n\ntext\n" + contentSize + " bytes";
                }
            } catch (Exception e) {
            }
        }
    }
    /**
     * 解析NDEF文本数据,从第三个字节开始,后面的文本数据
     * @param ndefRecord
     * @return
     */
    public static String parseTextRecord(NdefRecord ndefRecord) {
        /**
         * 判断数据是否为NDEF格式
         */
        //判断TNF
        if (ndefRecord.getTnf() != NdefRecord.TNF_WELL_KNOWN) {
            return null;
        }
        //判断可变的长度的类型
        if (!Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
            return null;
        }
        try {
            //获得字节数组,然后进行分析
            byte[] payload = ndefRecord.getPayload();
            //下面开始NDEF文本数据第一个字节,状态字节
            //判断文本是基于UTF-8还是UTF-16的,取第一个字节"位与"上16进制的80,16进制的80也就是最高位是1,
            //其他位都是0,所以进行"位与"运算后就会保留最高位
            String textEncoding = ((payload[0] & 0x80) == 0) ? "UTF-8" : "UTF-16";
            //3f最高两位是0,第六位是1,所以进行"位与"运算后获得第六位
            int languageCodeLength = payload[0] & 0x3f;
            //下面开始NDEF文本数据第二个字节,语言编码
            //获得语言编码
            String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
            //下面开始NDEF文本数据后面的字节,解析出文本
            String textRecord = new String(payload, languageCodeLength + 1,
                    payload.length - languageCodeLength - 1, textEncoding);
            return textRecord;
        } catch (Exception e) {
            throw new IllegalArgumentException();
        }
    }
5.写入NFC芯片
 @Override
    public void onNewIntent(Intent intent) {
        if (mText == null)
            return;
        //获取Tag对象
        Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        NdefMessage ndefMessage = new NdefMessage(
                new NdefRecord[] { createTextRecord(mText) });
        boolean result = writeTag(ndefMessage, detectedTag);
        if (result){
            Toast.makeText(this, "写入成功", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, "写入失败", Toast.LENGTH_SHORT).show();
        }
    }
    /**
     * 创建NDEF文本数据
     * @param text
     * @return
     */
    public static NdefRecord createTextRecord(String text) {
        byte[] langBytes = Locale.CHINA.getLanguage().getBytes(Charset.forName("US-ASCII"));
        Charset utfEncoding = Charset.forName("UTF-8");
        //将文本转换为UTF-8格式
        byte[] textBytes = text.getBytes(utfEncoding);
        //设置状态字节编码最高位数为0
        int utfBit = 0;
        //定义状态字节
        char status = (char) (utfBit + langBytes.length);
        byte[] data = new byte[1 + langBytes.length + textBytes.length];
        //设置第一个状态字节,先将状态码转换成字节
        data[0] = (byte) status;
        //设置语言编码,使用数组拷贝方法,从0开始拷贝到data中,拷贝到data的1到langBytes.length的位置
        System.arraycopy(langBytes, 0, data, 1, langBytes.length);
        //设置文本字节,使用数组拷贝方法,从0开始拷贝到data中,拷贝到data的1 + langBytes.length
        //到textBytes.length的位置
        System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length);
        //通过字节传入NdefRecord对象
        //NdefRecord.RTD_TEXT:传入类型 读写
        NdefRecord ndefRecord = new NdefRecord(NdefRecord.TNF_WELL_KNOWN,
                NdefRecord.RTD_TEXT, new byte[0], data);
        return ndefRecord;
    }
    /**
     * 写数据
     * @param ndefMessage 创建好的NDEF文本数据
     * @param tag 标签
     * @return
     */
    public static boolean writeTag(NdefMessage ndefMessage, Tag tag) {
        try {
            Ndef ndef = Ndef.get(tag);
            ndef.connect();
            ndef.writeNdefMessage(ndefMessage);
            return true;
        } catch (Exception e) {
        }
        return false;
    }

核心的代码已经贴出,查看详情的代码,请点击这里,已经简化成工具类,可直接拷贝使用。

Android读取和写入非NDEF格式的NFC芯片

对于非NDEF格式数据的NFC芯片,其中的数据格式是自定义的,但是存储到芯片中都是以字节码的形式。想深入学习的朋友可以,参考Github源码,编译生成的APK能够读取部分地区交通卡信息。


感谢以下知识的分享:
Android 高级开发——NFC标签开发深度解析
NFC百度百科

相关文章

  • Android-NFC

    本篇博客是为了解决以下的问题: NFC是什么 NFC的原理 NFC工作模式和实用场景 NFC相比于蓝牙的优势 An...

  • Android-NFC基础

    弄懂一个东西,必须知根知底,需知其然,知其所以然。 字面理解 NFC全称:Near Field Commun...

网友评论

      本文标题:Android-NFC

      本文链接:https://www.haomeiwen.com/subject/mhsiqftx.html