美文网首页
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

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