Android-NFC

本篇博客是為了解決以下的問(wèn)題:

  1. NFC是什么
  2. NFC的原理
  3. NFC工作模式和實(shí)用場(chǎng)景
  4. NFC相比于藍(lán)牙的優(yōu)勢(shì)
  5. Android讀取和寫入NDEF格式的NFC芯片
  6. Android讀取和寫入非NDEF格式的NFC芯片

NFC是什么

NFC是Near Field Communication的簡(jiǎn)稱断楷,意思為近距離無(wú)線通訊技術(shù),簡(jiǎn)單的說(shuō)是能夠在短距離之內(nèi)與兼容設(shè)備實(shí)現(xiàn)數(shù)據(jù)交換的技術(shù)。

NFC的原理

NFC是使用非接觸式射頻技術(shù)實(shí)現(xiàn)的數(shù)據(jù)交換技術(shù)掀鹅,在13.56MHz頻率20厘米距離內(nèi)進(jìn)行傳輸尊流,其傳輸速度有106 Kbit/秒鞍历、212 Kbit/秒或者424 Kbit/秒三種惹骂。

NFC工作模式和實(shí)用場(chǎng)景

NFC工作模式分為三種:

  • 讀卡器模式贮预,指含有NFC芯片的標(biāo)簽徙邻,可以被支持NFC的手機(jī)或者專門讀卡器設(shè)備讀取或者寫入數(shù)據(jù)排嫌。一般可以用于巡更,用支持NFC的手機(jī)到定點(diǎn)區(qū)讀取NFC芯片的數(shù)據(jù)然后傳輸?shù)胶笈_(tái)表示已巡視了定點(diǎn)區(qū)缰犁。
  • 仿真卡模式淳地,指含有IC卡的NFC芯片,可以使用支持NFC的手機(jī)或者NFC射頻器讀取出NFC芯片中IC卡的信息帅容。常見(jiàn)的由交通卡颇象,信用卡。
  • 點(diǎn)對(duì)點(diǎn)模式并徘,指兩臺(tái)NFC設(shè)備之間互相都可以進(jìn)行數(shù)據(jù) 交換遣钳。

NFC相比于藍(lán)牙的優(yōu)勢(shì)

藍(lán)牙4.0相比于以前版本,功耗低麦乞,傳輸速度快蕴茴,可以在10m之內(nèi)使用劝评。
NFC技術(shù)優(yōu)勢(shì)是創(chuàng)建連接快,傳輸速度也不慢倦淀,存儲(chǔ)芯片中的數(shù)據(jù)可以加密安全性較高蒋畜,在20cm之內(nèi)使用。

Android讀取和寫入NDEF格式的NFC芯片

首先說(shuō)明NFC芯片可以根據(jù)廠家定制撞叽,一次性寫入然后只能讀姻成,簡(jiǎn)單的可讀可寫。目前Android SDK API主要支持NFC論壇標(biāo)準(zhǔn)(Forum Standard)愿棋,這種標(biāo)準(zhǔn)被稱為NDEF科展。

1.支持NFC功能,添加權(quán)限
<uses-permission android:name="android.permission.NFC" />
<!-- 要求當(dāng)前設(shè)備必須要有NFC芯片 -->
<uses-feature android:name="android.hardware.nfc" android:required="true" />
2.當(dāng)前Activity的清單文件中的設(shè)置
<activity
    android:name="com.example.NFCActivity"
    android:launchMode="singleTask" >
    <intent-filter>    
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
3.當(dāng)前Activity聲明周期的判斷
private NfcAdapter mNfcAdapter;
private PendingIntent mPendingIntent;

@Override
    protected void onStart() {
        super.onStart();
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        //一旦截獲NFC消息糠雨,就會(huì)通過(guò)PendingIntent調(diào)用窗口
        mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()), 0);
    }
    /**
     * 獲得焦點(diǎn)辛润,按鈕可以點(diǎn)擊
     */
    @Override
    public void onResume() {
        super.onResume();
        //后面兩個(gè)null,null設(shè)置處理優(yōu)于所有其他NFC的處理
        if (mNfcAdapter != null)
            mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
    }
    /**
     * 暫停Activity,界面獲取焦點(diǎn)见秤,按鈕可以點(diǎn)擊
     */
    @Override
    public void onPause() {
        super.onPause();
        //恢復(fù)默認(rèn)狀態(tài)
        if (mNfcAdapter != null)
            mNfcAdapter.disableForegroundDispatch(this);
    }
4.讀取NFC芯片中內(nèi)容
@Override
    public void onNewIntent(Intent intent) {
        //1.獲取Tag對(duì)象
        Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        //2.獲取Ndef的實(shí)例
        Ndef ndef = Ndef.get(detectedTag);
        mTagText = ndef.getType() + "\nmaxsize:" + ndef.getMaxSize() + "bytes\n\n";
        readNfcTag(intent);
        mNfcText.setText(mTagText);
    }
    /**
     * 讀取NFC標(biāo)簽文本數(shù)據(jù)
     */
    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文本數(shù)據(jù)砂竖,從第三個(gè)字節(jié)開(kāi)始,后面的文本數(shù)據(jù)
     * @param ndefRecord
     * @return
     */
    public static String parseTextRecord(NdefRecord ndefRecord) {
        /**
         * 判斷數(shù)據(jù)是否為NDEF格式
         */
        //判斷TNF
        if (ndefRecord.getTnf() != NdefRecord.TNF_WELL_KNOWN) {
            return null;
        }
        //判斷可變的長(zhǎng)度的類型
        if (!Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
            return null;
        }
        try {
            //獲得字節(jié)數(shù)組鹃答,然后進(jìn)行分析
            byte[] payload = ndefRecord.getPayload();
            //下面開(kāi)始NDEF文本數(shù)據(jù)第一個(gè)字節(jié)乎澄,狀態(tài)字節(jié)
            //判斷文本是基于UTF-8還是UTF-16的,取第一個(gè)字節(jié)"位與"上16進(jìn)制的80测摔,16進(jìn)制的80也就是最高位是1置济,
            //其他位都是0,所以進(jìn)行"位與"運(yùn)算后就會(huì)保留最高位
            String textEncoding = ((payload[0] & 0x80) == 0) ? "UTF-8" : "UTF-16";
            //3f最高兩位是0锋八,第六位是1浙于,所以進(jìn)行"位與"運(yùn)算后獲得第六位
            int languageCodeLength = payload[0] & 0x3f;
            //下面開(kāi)始NDEF文本數(shù)據(jù)第二個(gè)字節(jié),語(yǔ)言編碼
            //獲得語(yǔ)言編碼
            String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
            //下面開(kāi)始NDEF文本數(shù)據(jù)后面的字節(jié)挟纱,解析出文本
            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對(duì)象
        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();
        }
    }
    /**
     * 創(chuàng)建NDEF文本數(shù)據(jù)
     * @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");
        //將文本轉(zhuǎn)換為UTF-8格式
        byte[] textBytes = text.getBytes(utfEncoding);
        //設(shè)置狀態(tài)字節(jié)編碼最高位數(shù)為0
        int utfBit = 0;
        //定義狀態(tài)字節(jié)
        char status = (char) (utfBit + langBytes.length);
        byte[] data = new byte[1 + langBytes.length + textBytes.length];
        //設(shè)置第一個(gè)狀態(tài)字節(jié)羞酗,先將狀態(tài)碼轉(zhuǎn)換成字節(jié)
        data[0] = (byte) status;
        //設(shè)置語(yǔ)言編碼,使用數(shù)組拷貝方法紊服,從0開(kāi)始拷貝到data中檀轨,拷貝到data的1到langBytes.length的位置
        System.arraycopy(langBytes, 0, data, 1, langBytes.length);
        //設(shè)置文本字節(jié),使用數(shù)組拷貝方法欺嗤,從0開(kāi)始拷貝到data中参萄,拷貝到data的1 + langBytes.length
        //到textBytes.length的位置
        System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length);
        //通過(guò)字節(jié)傳入NdefRecord對(duì)象
        //NdefRecord.RTD_TEXT:傳入類型 讀寫
        NdefRecord ndefRecord = new NdefRecord(NdefRecord.TNF_WELL_KNOWN,
                NdefRecord.RTD_TEXT, new byte[0], data);
        return ndefRecord;
    }
    /**
     * 寫數(shù)據(jù)
     * @param ndefMessage 創(chuàng)建好的NDEF文本數(shù)據(jù)
     * @param tag 標(biāo)簽
     * @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;
    }

核心的代碼已經(jīng)貼出,查看詳情的代碼煎饼,請(qǐng)點(diǎn)擊這里讹挎,已經(jīng)簡(jiǎn)化成工具類,可直接拷貝使用。

Android讀取和寫入非NDEF格式的NFC芯片

對(duì)于非NDEF格式數(shù)據(jù)的NFC芯片筒溃,其中的數(shù)據(jù)格式是自定義的马篮,但是存儲(chǔ)到芯片中都是以字節(jié)碼的形式。想深入學(xué)習(xí)的朋友可以铡羡,參考Github源碼积蔚,編譯生成的APK能夠讀取部分地區(qū)交通卡信息意鲸。


感謝以下知識(shí)的分享:
Android 高級(jí)開(kāi)發(fā)——NFC標(biāo)簽開(kāi)發(fā)深度解析
NFC百度百科

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末烦周,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子怎顾,更是在濱河造成了極大的恐慌读慎,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件槐雾,死亡現(xiàn)場(chǎng)離奇詭異夭委,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)募强,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門株灸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人擎值,你說(shuō)我怎么就攤上這事慌烧。” “怎么了鸠儿?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵屹蚊,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我进每,道長(zhǎng)汹粤,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任田晚,我火速辦了婚禮嘱兼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘贤徒。我一直安慰自己遭京,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布泞莉。 她就那樣靜靜地躺著哪雕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鲫趁。 梳的紋絲不亂的頭發(fā)上斯嚎,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼堡僻。 笑死糠惫,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的钉疫。 我是一名探鬼主播硼讽,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼牲阁!你這毒婦竟也來(lái)了固阁?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤城菊,失蹤者是張志新(化名)和其女友劉穎备燃,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體凌唬,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡并齐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了客税。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片况褪。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖更耻,靈堂內(nèi)的尸體忽然破棺而出测垛,到底是詐尸還是另有隱情,我是刑警寧澤酥夭,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布赐纱,位于F島的核電站,受9級(jí)特大地震影響熬北,放射性物質(zhì)發(fā)生泄漏疙描。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一讶隐、第九天 我趴在偏房一處隱蔽的房頂上張望起胰。 院中可真熱鬧,春花似錦巫延、人聲如沸效五。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)畏妖。三九已至,卻和暖如春疼阔,著一層夾襖步出監(jiān)牢的瞬間戒劫,已是汗流浹背半夷。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留迅细,地道東北人巫橄。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像茵典,于是被迫代替她去往敵國(guó)和親湘换。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容