Android NFC問(wèn)道

概述

NFC(Near Field Communication)藐鹤,無(wú)線近場(chǎng)通訊技術(shù)各吨。

  • 技術(shù)指標(biāo)
  • 初始連接距離 :<= 4cm
  • 通訊距離 : 10cm (理論)
  • 射頻頻率 : 13.56MHz
  • 傳輸速率 :106 / 212 / 424 kbps

應(yīng)用

Bridging The Physical and Virtual World恃锉,NFC 是現(xiàn)實(shí)世界與虛擬世界的橋梁 -- Google IO

  • 生活中的 NFC應(yīng)用

  • 讀寫(xiě)信息,通過(guò) NFC 讀取海報(bào)上的電子標(biāo)簽(射頻識(shí)別信息)等

  • 數(shù)據(jù)交換束铭,不同 NFC設(shè)備間建立連接后進(jìn)行數(shù)據(jù)交換炭分,手機(jī)間發(fā)送圖片,音樂(lè)等,和藍(lán)牙類(lèi)似

  • 內(nèi)置卡片莲绰,手機(jī) NFC 功能支持公交刷卡欺旧,門(mén)禁刷卡等

  • 基于此,Android NFC支持三種功能操作模式

  • 讀卡器模式(Reader/Writer Mode) 蛤签,允許 NFC設(shè)備讀/寫(xiě)電子標(biāo)簽(RFID,射頻識(shí)別)

  • P2P模式(P2P Mode)辞友,建立連接的 NFC 設(shè)備間進(jìn)行端到端的數(shù)據(jù)交換/傳輸

  • 模擬卡模式(Card Emulation Mode),允許 NFC設(shè)備內(nèi)置IC卡信息震肮,充當(dāng) IC 卡称龙,模擬卡模式供電方式由讀卡器的 供電,手機(jī)關(guān)機(jī)時(shí)亦可工作

開(kāi)發(fā)與運(yùn)用

NFC基礎(chǔ)

簡(jiǎn)介在 Android 平臺(tái)上運(yùn)行的 NFC 任務(wù)戳晌,及其如何從以NDEF ( 用于 NFC數(shù)據(jù)交互的標(biāo)準(zhǔn)數(shù)據(jù)格式 ) 格式接收或發(fā)送 NFC 數(shù)據(jù)鲫尊,與支持其操作的 Android API
官文 :https://developer.android.com/guide/topics/connectivity/nfc/nfc.html

  • NDEF兩種主要用途
  • 基于 Tag調(diào)度系統(tǒng)(Tag Dispatch System )從 NFC tag 中讀取 NDEF 數(shù)據(jù)
  • 基于 Android Beam 從一臺(tái)設(shè)備向另一臺(tái)設(shè)備播送 NDEF 信息

Tag調(diào)度系統(tǒng)

開(kāi)啟了 NFC 功能的設(shè)備,在亮屏狀態(tài)下沦偎,會(huì)在允許范圍能定時(shí)監(jiān)測(cè) NFC tag疫向。當(dāng)成功捕捉到 NFC tag 時(shí),由于 NFC 的短距離檢測(cè)性質(zhì)扛施,Tag調(diào)度系統(tǒng)會(huì)立即在最合適的 Activity 中將 tag 封裝于 intent 中鸿捧,不發(fā)出允許請(qǐng)求或者尋找將要使用的NFC的 Activity,以防 NFC tag 在距離增加時(shí)無(wú)法捕捉疙渣。

  • 解析 tag 信息及將 tag 發(fā)送到指定應(yīng)用
  • 解析標(biāo)識(shí) MIME 或 URI 的信息
  • 將 MIME/URI 及其他信息封裝到 intent
  • 基于 intent 啟動(dòng)指定 activity, 將 tags分配給應(yīng)用程序
    詳見(jiàn)下文

NFC tag 如何映射 MIME 和 URI

類(lèi)似計(jì)算機(jī)網(wǎng)絡(luò)匙奴,NFC 的無(wú)線數(shù)據(jù)交互亦基于協(xié)議完成,什么協(xié)議官文并未給出定義妄荔,但有標(biāo)準(zhǔn)格式報(bào)文** NDEF **泼菌。 類(lèi)似網(wǎng)絡(luò)中的 IP 數(shù)據(jù)報(bào),報(bào)文首部中的特定 bits 用于標(biāo)識(shí)啦租,報(bào)文主體搭載數(shù)據(jù)哗伯。
?NDEF 以 NdefRecord 對(duì)象的形式被封裝于 NdefMessage 中,一個(gè) NdefMessage 可包含多個(gè) NdefRecord篷角,NdefMessage 可視為 NdefRecord 的容器, NdefMessage 類(lèi)比 IP數(shù)據(jù)報(bào)焊刹, 第一個(gè) NdefRecord 類(lèi)比 IP 數(shù)據(jù)報(bào)首部。

  • 標(biāo)準(zhǔn)數(shù)據(jù)格式下恳蹲,NDEF message中的第一個(gè)NDEF record數(shù)據(jù)格式簡(jiǎn)析
  • 3-bit TNF(Type Name Format), 指明 Variable length type 域的數(shù)據(jù)類(lèi)型
  • Variable length type, 指明record (NdefRecord) 的類(lèi)型
  • Variable length ID, record 唯一標(biāo)識(shí)虐块,不常用
  • Variable length payload, 實(shí)際要進(jìn)行讀寫(xiě)的數(shù)據(jù)

Tag調(diào)度系統(tǒng)通過(guò) TNF和 Variable length type解析出 MIME/URI 并進(jìn)行匹配,如果成功嘉蕾,將其隨著 payload 數(shù)據(jù)一同封裝進(jìn) ACTION_NDEF_DISCOVERY intent 中贺奠。否則,tag 對(duì)象含有的技術(shù)信息(technologies)及其搭載的數(shù)據(jù)將被封裝到 ACTION_TECH_DISCOVERY intent中错忱。

將 NFC tags 分派給應(yīng)用程序

當(dāng)Tag調(diào)度系統(tǒng)成功創(chuàng)建封裝有 NFC tag 的 intent后儡率,需將 intent傳給有相關(guān)應(yīng)用程序挂据,應(yīng)用程序需定義 filter屬性與 intent進(jìn)行匹配。如果有多個(gè)應(yīng)用程序能夠處理此 intent儿普,則有 Activity Chooser進(jìn)行選擇崎逃。

  • 優(yōu)先級(jí)從高到低的三個(gè) intent
  • ACTION_NDEF_DISCOVERED, 優(yōu)先級(jí)最高的 intent
  • ACTION_TECH_DISCOVERED, 優(yōu)先級(jí)次之,當(dāng)無(wú)注冊(cè) ACTION_NDEF_DISCOVERED箕肃,或者無(wú)法匹配 MIME/URI 號(hào)婚脱,或者是一個(gè) 沒(méi)有包含 NDEF數(shù)據(jù)technolog tag 時(shí)今魔,啟動(dòng)此 intent
  • ACTION_TAG_DISCOVERED, 優(yōu)先級(jí)最低勺像,用于前兩種情況均無(wú)法應(yīng)用時(shí)

流程圖如下:

Tag Dispatch System

*Note : 有使用 NDEF message 時(shí),應(yīng)創(chuàng)建 ACTION_NDEF_DISCOVERY intent *


實(shí)戰(zhàn)演練, 以讀卡器模式為例

Manifest

  • NFC權(quán)限請(qǐng)求
<uses-permission android:name="android.permission.NFC" />
  • 最低 Sdk 版本
<uses-sdk android:minSdkVersion="10"/>
  • 特征
 <uses-feature android:name="android.hardware.nfc" android:required="true" />
  • 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_list"/>

 <intent-filter>
      <action android:name="android.nfc.action.TAG_DISCOVERED"/>

      <category android:name="android.intent.category.DEFAULT"/>
 </intent-filter>

創(chuàng)建res/xml/nfc_tech_filter.xml

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <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.MifareClassic</tech>
    </tech-list>
    <tch-list>
        <tech>android.nfc.tech.NdefFormatable</tech>
    </tch-list>
</resources>

MainActivity.java

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

        Intent nfcIntent = new Intent(this, getClass());
        mTextView = (TextView)findViewById(R.id.text_view);
        nfcIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
      
        mPendingIntent = PendingIntent.getActivity(this, 0, nfcIntent, 0);
        mAdapter = NfcAdapter.getDefaultAdapter(this);
        if (mAdapter == null){
            Toast.makeText(getApplicationContext(), "此設(shè)備不支持 NFC ", Toast.LENGTH_SHORT).show();
            return;
        }
        if(!mAdapter.isEnabled()) {
            Toast.makeText(getApplicationContext(), " NFC 未開(kāi)啟 ", Toast.LENGTH_SHORT).show();
        }else{
            Toast.makeText(getApplicationContext(), " 請(qǐng)將預(yù)讀卡片緊貼手機(jī)背部 ", Toast.LENGTH_SHORT).show();
        }
    }

More Info about PendingIntent http://blog.csdn.net/harvic880925/article/details/42030955

  • OnResume()
@Override
    protected void onResume() {
        super.onResume();
       //enableForegroundDispatch()允許 app前臺(tái)調(diào)度當(dāng)前Activity错森,而不通過(guò)系統(tǒng)自帶的 Activity Choose Dialog 進(jìn)行選擇
        mAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
    }
  • OnNewIntent()
//當(dāng) Activity 中有新 intent 創(chuàng)建時(shí)調(diào)用
 @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        getTagInfo(intent);
        mTextView.setText(mInfo);
    }
  • OnPause()
@Override
    protected void onPause() {
        super.onPause();
        if (mAdapter != null) {
            mAdapter.disableForegroundDispatch(this);
        }
    }
  • MyFuncs
  private void getTagInfo(Intent intent){
        //獲取 Tag 對(duì)象數(shù)據(jù)吟宦,用于解析出 id 和 所適配的 tech
        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        byte[] tagId = tag.getId();
        //將 byte[] 轉(zhuǎn)換為 十六進(jìn)制字符
        String idStr = FormatUtil.ByteArrayToHexString(tagId);

        mInfo = "TypeID : " + idStr + "\n";
        // 獲取 tag 中能于 android nfc 適配的 tech
        for (int i = 0; i < tag.getTechList().length; i++){
            mInfo += "Tech-" + (i + 1) + " : "+ tag.getTechList()[i] + "\n";
        }
        Toast.makeText(getApplicationContext(), idStr, Toast.LENGTH_SHORT).show();
    }

?此例能讀取多種類(lèi)型的 Tag 數(shù)據(jù),小編試過(guò)銀行卡涩维,羊城通公交卡殃姓,學(xué)生飯卡及身份證。但讀取的信息有限瓦阐,而且無(wú)用... 連卡號(hào)無(wú)法讀取蜗侈,后續(xù)基于公交卡適配的 IsoDep tech 查找相關(guān)資料,進(jìn)行如下擴(kuò)展睡蟋。

擴(kuò)展

IsoDep 通過(guò) Byte[] transceive(Byte[] cmdData) 函數(shù)進(jìn)行交互踏幻,函數(shù)參數(shù)是 命令數(shù)據(jù),返回結(jié)果數(shù)據(jù)
詳見(jiàn) https://developer.android.com/reference/android/nfc/tech/NfcA.html#transceive(byte[])

public class Yangchengtong {
    public static class SendCmd{
        public static byte[] id = { (byte) 'P', (byte) 'A', (byte) 'Y',
                (byte) '.', (byte) 'A', (byte) 'P', (byte) 'P', (byte) 'Y'};
        public static byte[] balance = {
                (byte) 0x80,
                (byte) 0x5C,
                (byte) 0x00,
                (byte) 0x02,
                (byte) 0x04
              }; //通信后獲取的 byte[] 取前四個(gè)字節(jié)轉(zhuǎn)換成int 再除以 100 得到余額
        public static byte[] trades = {
                (byte) 0x00,
                (byte) 0xB2,
                (byte) 0x01,
                (byte) 0xC5,
                (byte) 0x00
              }; //一次性讀取所有交易記錄
        }
}
  • MainActivity.java 續(xù)
private void getPreciseTagInfo(Intent intent){
        IsoDep isoDep = IsoDep.get((Tag)intent.getParcelableExtra(NfcAdapter.EXTRA_TAG));
        try{
            isoDep.connect();
            byte[] cardNameBytes = isoDep.transceive(Yangchengtong.SendCmd.id);
            mInfo += divideLine;
            mInfo += "Card Type Bytes: " + FormatUtil.ByteArrayToHexString(cardNameBytes) + "\n";

            byte[] cardBalanceBytes = isoDep.transceive(Yangchengtong.SendCmd.balance);
            mInfo += "Card Balance Bytes: " + FormatUtil.ByteArrayToHexString(cardBalanceBytes) + "\n";

        }catch (IOException ioe){
            Log.e("Error connect isoDep", ioe.toString());
        }finally {
            if(isoDep != null){
                try{
                    isoDep.close();
                }catch (IOException e){
                    Log.e("Error close isoDep", e.toString());
                }
            }
        }
    }

? em... 能成功讀出支持 IsoDep IC卡的數(shù)據(jù)该面,但經(jīng)驗(yàn)證,余額不準(zhǔn)確信卡。無(wú)法找到羊城通與 NFC交互的相關(guān)資料隔缀,至此暫時(shí)冰凍此次嘗試。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末傍菇,一起剝皮案震驚了整個(gè)濱河市猾瘸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌丢习,老刑警劉巖牵触,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異泛领,居然都是意外死亡荒吏,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)渊鞋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)绰更,“玉大人瞧挤,你說(shuō)我怎么就攤上這事±芡澹” “怎么了特恬?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)徐钠。 經(jīng)常有香客問(wèn)我癌刽,道長(zhǎng),這世上最難降的妖魔是什么尝丐? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任显拜,我火速辦了婚禮,結(jié)果婚禮上爹袁,老公的妹妹穿的比我還像新娘远荠。我一直安慰自己,他們只是感情好失息,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布譬淳。 她就那樣靜靜地躺著,像睡著了一般盹兢。 火紅的嫁衣襯著肌膚如雪邻梆。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天绎秒,我揣著相機(jī)與錄音浦妄,去河邊找鬼。 笑死替裆,一個(gè)胖子當(dāng)著我的面吹牛校辩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播辆童,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼宜咒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了把鉴?” 一聲冷哼從身側(cè)響起故黑,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎庭砍,沒(méi)想到半個(gè)月后场晶,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡怠缸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年诗轻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片揭北。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扳炬,死狀恐怖吏颖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情恨樟,我是刑警寧澤半醉,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站劝术,受9級(jí)特大地震影響缩多,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜养晋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一衬吆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧匙握,春花似錦咆槽、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)麦射。三九已至蛾娶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間潜秋,已是汗流浹背蛔琅。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留峻呛,地道東北人罗售。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像钩述,于是被迫代替她去往敵國(guó)和親寨躁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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