Android開發(fā) - NFC基礎(chǔ)

NFC 基礎(chǔ)

本文檔介紹了在Android上的基本的NFC任務(wù)。它說明了如何發(fā)送和接收的NDEF消息(NDEF messages)的形式的表單里包含的NFC數(shù)據(jù)(NFC data)鳞陨,并介紹Android框架里支持這些功能的API。對于更高級的主題葛作,包括與非NDEF數(shù)據(jù)的討論,請參閱高級NFC。

當(dāng)使用NDEF 數(shù)據(jù)和Android時菠齿,有兩個主要的使用場景:

?從一個NFC 標(biāo)簽里讀取NDEF 數(shù)據(jù)

?使用 Android Beam? 快速傳輸Beaming NDEF messages從一臺設(shè)備到另一臺

<譯者注:Android Beam是android系統(tǒng)的一個傳輸套件志膀,對于具有NFC設(shè)備的手機(jī)熙宇,可通過該功能在兩臺手機(jī)之間傳輸聯(lián)系人圖片等數(shù)據(jù),使用方法也很簡單溉浙,將兩個手機(jī)背靠背烫止,這時候位于上面的手機(jī)會提示你“敲擊屏幕即可穿數(shù)據(jù)”>

從一個NFC tag里讀取NDEF data 將會經(jīng)過 tag dispatch system,分析被發(fā)現(xiàn)的NFC標(biāo)簽戳稽,進(jìn)行適當(dāng)?shù)臄?shù)據(jù)歸類馆蠕,并啟動一個對該分類的數(shù)據(jù)感興趣的應(yīng)用程序。那些想要處理被掃描到的NFC標(biāo)簽的應(yīng)用程序可以聲明一個意圖過濾器(declare an intent filter )惊奇,并要求處理這些數(shù)據(jù)互躬。

Android Beam?功能,它允許通過輕輕敲擊設(shè)備的方式赊时,推送一個NDEF message從一臺設(shè)備到另一臺設(shè)備上吨铸。這種相互作用提供了一個簡單的方法來發(fā)送數(shù)據(jù),比其它無線技術(shù)祖秒,比如藍(lán)牙诞吱,因為有了NFC舟奠,不再需要手動設(shè)置發(fā)現(xiàn)或配對(譯者注:藍(lán)牙需要設(shè)置搜索設(shè)備和配對)。當(dāng)兩個設(shè)備進(jìn)入范圍內(nèi)(譯者注:NFC要求在幾厘米內(nèi))房维,自動啟動連接沼瘫。 Android Beam可通過一組NFC API被使用,因此咙俩,任何應(yīng)用都可以在設(shè)備之間傳輸信息耿戚。例如,聯(lián)系人阿趁,瀏覽器和YouTube應(yīng)用程序使用Android Beam與其他設(shè)備共享聯(lián)系人膜蛔,網(wǎng)頁和視頻。

標(biāo)簽分發(fā)系統(tǒng) (The Tag Dispatch System)

Android的設(shè)備通常是在屏幕解鎖的時候?qū)ふ襈FC標(biāo)簽脖阵,除非在設(shè)置菜單中禁用了NFC設(shè)備皂股。當(dāng)Android手機(jī)發(fā)現(xiàn)了一個NFC標(biāo)簽,所希望的行為是最適當(dāng)?shù)腶ctivity來處理它命黔,而不是要求用戶選擇處理它的應(yīng)用程序呜呐。因為設(shè)備掃描NFC標(biāo)簽,在很短的范圍內(nèi)悍募,它很可能導(dǎo)致當(dāng)用戶手動選擇(處理該tag的應(yīng)用)時強(qiáng)行移動設(shè)備遠(yuǎn)離標(biāo)簽而斷開連接蘑辑。您最好讓你開發(fā)的應(yīng)用程序僅僅關(guān)注您制定的NFC標(biāo)簽,以防止用戶手動選擇處理的活動的頁面出現(xiàn)坠宴。

為了幫助你實(shí)現(xiàn)這個目標(biāo)洋魂,Android提供了一個特殊的標(biāo)簽分發(fā)系統(tǒng),它會分析被掃描到的NFC標(biāo)簽啄踊,解析他們忧设,并試圖定位到對這個被掃描到的標(biāo)簽感興趣的應(yīng)用程序。這是通過:

1颠通。解析NFC標(biāo)簽和搞清楚MIME類型,或者被包含在標(biāo)簽中的有標(biāo)記的一個URI膀懈。

2顿锰。先封裝MIME類型或URI,在裝入一個intent內(nèi)启搂。前兩個步驟中描述了NFC標(biāo)簽是如何映射到MIME類型和URI的硼控。

3。使用封裝好的intent啟動應(yīng)用程序胳赌。這是描述如何將NFC標(biāo)簽分派到對其感興趣的應(yīng)用程序牢撼。

<譯者注:android 的標(biāo)簽分發(fā)系統(tǒng)做了下面一些事情:解析標(biāo)簽里的數(shù)據(jù),并裝入intent內(nèi)疑苫,并啟動關(guān)注該類型的標(biāo)簽的應(yīng)用程序>

NFC 標(biāo)簽是如何被映射到 MIME 類型和URIs 的

NFC標(biāo)簽是如何映射到MIME類型和URI

在你開始寫你的NFC應(yīng)用之前熏版,重要的是要了解不同類型的NFC標(biāo)簽纷责,標(biāo)簽分發(fā)系統(tǒng)如何解析NFC標(biāo)簽,當(dāng)它(標(biāo)簽分發(fā)系統(tǒng))檢測到一個NDEF消息后如何分發(fā)到應(yīng)用程序撼短。NFC標(biāo)簽是一種比較廣泛的技術(shù)(譯者注:標(biāo)簽的種類樣式多)再膳,也有許多不同的數(shù)據(jù)寫入方式。 Android最大化的支持NDEF標(biāo)準(zhǔn)曲横,它是由NFC論壇(NFC Forum.)定義的喂柒。

NDEF數(shù)據(jù)被封裝一個消息(NdefMessage)的內(nèi)部,一個消息包含一個或多個的記錄(NdefRecord)禾嫉。每個NDEF記錄必須是格式有效的灾杰,符合規(guī)范的。當(dāng)然熙参,你的NDEF記錄也可以符合你創(chuàng)建的類型的規(guī)范艳吠。 Android還支持其他不包含NDEF數(shù)據(jù)的標(biāo)簽,您可以通過使用包含在android.nfc.tech包的類來實(shí)現(xiàn)它尊惰。要了解有關(guān)這些技術(shù)的更多信息讲竿,請參見高級NFC主題。工作涉及到編寫自己的協(xié)議棧與這些其他類型的標(biāo)簽進(jìn)行通信弄屡,因此我們建議在可能易于開發(fā)的情況下使用NDEF和采用Android的設(shè)備的最大支持题禀。

注意:要下載完整的NDEF規(guī)格,請到NFC Forum Specification Download 頁面下載和查看 《創(chuàng)建一般類型的NDEF記錄Creating common types of NDEF records 》尋找如何建設(shè)NDEF記錄的示例膀捷。

現(xiàn)在迈嘹,你有NFC標(biāo)簽的一些背景知識,下面的章節(jié)更詳細(xì)描述了Android是如何處理NDEF格式的標(biāo)簽的全庸。當(dāng)Android手機(jī)掃描一個包含了NDEF格式數(shù)據(jù)的NFC標(biāo)簽秀仲,解析消息,并試圖找出數(shù)據(jù)的MIME類型或標(biāo)識URI壶笼。要做到這一點(diǎn)神僵,系統(tǒng)讀取Ndef Message里面的第一個NdefRecord,以確定如何解釋整個NDEF消息(NDEF消息可以包含多個NDEF記錄)覆劈。在一個格式良好的NDEF消息中保礼,第一個NdefRecord包含以下字段:

3-bit TNF (Type Name Format) - 類型名格式

指示如何解釋變量長度類型字段。有效的值記載在表1中描述的责语。

變量長度類型

描述記錄類型炮障。如果使用TNF_WELL_KNOWN,使用此字段指定的記錄類型定義(RTD)坤候。有效的RTD值描述于表2中胁赢。

變量長度ID

記錄的唯一標(biāo)識符。此字段不經(jīng)常使用白筹,但如果您需要一個具有唯一標(biāo)識的標(biāo)簽智末,你可以創(chuàng)建一個ID來這么做谅摄。

變量長度的有效載荷

要讀取或?qū)懭氲膶?shí)際數(shù)據(jù)負(fù)載。 一個NDEF消息可以包含多個NDEF記錄吹害,所以不要以為全部負(fù)載存在于這個NDEF消息的第一條NDEF紀(jì)錄內(nèi)螟凭。

標(biāo)簽分發(fā)系統(tǒng)使用TNF和類型字段映射MIME類型或URI到一個NDEF消息。如果成功的話它呀,它封裝的信息位于一個ACTION_NDEF_DISCOVERED intent內(nèi)部螺男,連同那些實(shí)際的有效載荷。但是纵穿,也有標(biāo)記調(diào)度系統(tǒng)不能從第一條NDEF記錄里確定數(shù)據(jù)的類型情下隧。發(fā)生這種情況時,NDEF數(shù)據(jù)不能被映射到一個MIME類型或URI谓媒,或著當(dāng)NFC標(biāo)簽不以NDEF數(shù)據(jù)作為開始淆院。在這種情況下,一個標(biāo)簽對象(Tag object)句惯,該對象具有有關(guān)標(biāo)簽內(nèi)容的技術(shù)信息和有效載荷土辩,將會被封裝到一個ACTION_TECH_DISCOVERED intent內(nèi)。

表1中抢野。介紹了標(biāo)簽分發(fā)系統(tǒng)如何映射TNF和類型字段到MIME類型或URIs拷淘。同時也說明了哪些TNFs不能被映射到MIME類型或URI。在這種情況下指孤,標(biāo)簽分發(fā)系統(tǒng)將回退到ACTION_TECH_DISCOVERED的方式启涯。

例如,如果標(biāo)簽分發(fā)系統(tǒng)遇到一個TNF_ABSOLUTE_URI類型的紀(jì)錄恃轩,將該記錄的變量長度類型字段映射到一個URI结洼。標(biāo)簽分發(fā)系統(tǒng)封裝封裝那個URI到一個 ACTION_NDEF_DISCOVERED intent的一個數(shù)據(jù)字段內(nèi),以及與其他的標(biāo)簽信息叉跛,比如其他實(shí)際負(fù)載松忍。另一方面,如果它遇到的記錄類型是TNF_UNKNOWN筷厘,它將創(chuàng)建標(biāo)簽的技術(shù)信息的封裝挽铁。

Table 1. 支持 TNFs and 對應(yīng)的映射

Paste_Image.png

Table 2. 支持 RTDs 關(guān)于 TNF_WELL_KNOWN 和對應(yīng)的 映射


Paste_Image.png

NFC標(biāo)簽是怎樣被分派到應(yīng)用程序的

當(dāng)標(biāo)簽分發(fā)系統(tǒng)創(chuàng)建了一個封裝了NFC標(biāo)簽和它的識別信息的 intent,標(biāo)簽分發(fā)系統(tǒng)會將該intent發(fā)送到添加了該intent 的過濾器(intent filter)的應(yīng)用程序敞掘。如果一個以上的應(yīng)用程序可以處理的該intent,活動選擇器將被啟動楣铁,使得用戶可以選擇哪一個應(yīng)用程序玖雁。標(biāo)簽調(diào)度系統(tǒng)定義了三種intent,下面從最高優(yōu)先級到最低優(yōu)先級的順序展示它們:

ACTION_NDEF_DISCOVERED:當(dāng)一個標(biāo)簽被掃描盖腕,包含有NDEF有效載荷或者是一個可識別的類型赫冬,將會創(chuàng)建一個這樣的intent 用來啟動一個應(yīng)用程序浓镜。這是最高優(yōu)先級的意圖,標(biāo)簽分發(fā)系統(tǒng)會盡可能的使用這種intent而不使用其他方式來啟動一個應(yīng)用劲厌。<譯者注:盡可能直接對應(yīng)一個應(yīng)用而防止出現(xiàn)讓用戶選擇活動>
ACTION_TECH_DISCOVERED:如果沒有活動注冊處理ACTION_NDEF_DISCOVERED intent膛薛,標(biāo)簽分發(fā)系統(tǒng)嘗試用這個意圖啟動應(yīng)用程序。如果標(biāo)簽被掃描包含有NDEF數(shù)據(jù)但不能被映射到一個MIME類型或URI补鼻,或者如果標(biāo)簽不包含NDEF數(shù)據(jù)但同時是一個已知的標(biāo)簽技術(shù)哄啄,此intent也可直接被啟動(沒有ACTION_NDEF_DISCOVERED優(yōu)先)。
ACTION_TAG_DISCOVERED:如果沒有活動處理ACTION_NDEF_DISCOVERED 或者 ACTION_TECH_DISCOVERED的inent,那么這個inent將被啟動风范。.
標(biāo)簽分發(fā)系統(tǒng)的工作原理咨跌,基本方法如下:

標(biāo)簽分發(fā)系統(tǒng)解析NFC的標(biāo)簽,創(chuàng)建inten,嘗試啟動一個應(yīng)用程序(無論是ACTION_NDEF_DISCOVERED或ACTION_TECH_DISCOVERED)的硼婿。
如果沒有應(yīng)用的過濾器(intent filter)捕獲它锌半,嘗試使用下一個低優(yōu)先級的intent啟動一個應(yīng)用程序(eitherACTION_TECH_DISCOVERED或ACTION_TAG_DISCOVERED),直到被應(yīng)用程序的過濾器捕獲寇漫,或者直到標(biāo)簽分發(fā)度系統(tǒng)嘗試了所有可能的intent刊殉。
如果沒有應(yīng)用程序的intent過濾器(inent filter)捕獲該intent,什么也不做.
<譯者注:android可以在系統(tǒng)配置文件指定某個activity的intent filter州胳,標(biāo)簽分發(fā)系統(tǒng)會嘗試構(gòu)建intent來直接對應(yīng)它记焊,如果失敗,就再次嘗試更低級別的inent來嘗試陋葡,直到發(fā)現(xiàn)沒有任何一個來處理它>

圖 1. 標(biāo)簽分發(fā)系統(tǒng)

只要有可能亚亲,請使用NDEF消息和ACTION_NDEF_DISCOVERED的inent來達(dá)成你的工作,因為它是三種inent里最特殊的一個腐缤。相比其他兩種intent捌归,使用這個intent,您可以啟動您的應(yīng)用程序在一個更合適的時間岭粤,從而給用戶帶來更好的體驗.

<譯者注:因為ACTION_NDEF_DISCOVERED intent比較特殊惜索,才更加的不容易發(fā)生用戶不得不手動選擇處理該標(biāo)簽的程序的情況出現(xiàn)>

在Android清單文件(Manifest)里要求NFC訪問

在您訪問NFC硬件設(shè)備和妥善處理NFC的intent之前,在你的AndroidManifest.xml文件里聲明這些項:

?NFC的硬件訪問權(quán)限<uses-permission>:

<uses-permission android:name="android.permission.NFC"/>

?您的應(yīng)用程序可以支持的最低SDK版本剃浇。 API級別9只支持通過ACTION_TAG_DISCOVERED有限的標(biāo)簽分發(fā)巾兆,只給訪問NDEF的消息使用EXTRA_NDEF_MESSAGES進(jìn)行額外的擴(kuò)展。沒有其他變量的屬性或I/ O操作可以訪問虎囚。 API 10級包括全面的讀/寫支持以及前臺NDEF推入角塑, API 14級提供了一個簡單的使用Android Beam 對NDEF消息到其他設(shè)備的方式來推入和額外的方便的方法創(chuàng)建NDEF記錄。

<uses-sdk android:minSdkVersion="10"/>

用戶特性 uses-feature 元素的聲明淘讥,使得當(dāng)你的應(yīng)用程序Google Play 里出現(xiàn)時圃伶,僅僅被具有NFC硬件的手機(jī)設(shè)備看到:
<uses-feature android:name="android.hardware.nfc" android:required="true" />

如果你的程序里的NFC功能不是必須得主要功能,那么可以忽略上面這個用戶特性 uses-feature 。你可以在程序運(yùn)行時檢測是否具有NFC硬件設(shè)備窒朋,使用getDefaultAdapter() 方法搀罢,如果為空,就是沒有NFC設(shè)備侥猩。.

NFC Intents的過濾器

一個你關(guān)注的NFC標(biāo)簽進(jìn)行被掃描盜時榔至,您的應(yīng)用程序啟動處理,您的應(yīng)用程序可以在清單文件中聲明過濾一種欺劳,兩種或全部三種NFC intent唧取。但是,當(dāng)應(yīng)用程序啟動時杰标,您通常要篩選ACTION_NDEF_DISCOVERED意圖為了更多的控制兵怯。ACTION_TECH_DISCOVERED意圖是一個備用的方式,當(dāng)ACTION_NDEF_DISCOVERED時沒有被注冊到過濾器腔剂,或者有效載荷數(shù)據(jù)不是NDEF數(shù)據(jù)時媒区。過濾ACTION_TAG_DISCOVERED的intnet屬于過于籠統(tǒng)的過濾器類別。許多應(yīng)用程序會在ACTION_TAG_DISCOVERED之前掸犬,先過濾ACTION_NDEF_DISCOVERED或ACTION_TECH_DISCOVERED袜漩,所以您的應(yīng)用程序啟動的概率很低。ACTION_TAG_DISCOVERED僅可作為最后的手段湾碎,僅僅在沒有其他過濾了ACTION_NDEF_DISCOVERED或ACTION_TECH_DISCOVERED的意圖的應(yīng)用程序被安裝的情況下才有用宙攻。

因為NFC標(biāo)簽的多樣化和多次性,往往不在你的控制之下介褥,這并不總是可能的座掘,這就是為什么你在必要時需要備選其他兩種意圖。當(dāng)你需要控制標(biāo)簽的類型和數(shù)據(jù)寫入柔滔,建議您使用NDEF格式的標(biāo)簽溢陪。以下各節(jié)描述了如何過濾每種類型的意圖.

ACTION_NDEF_DISCOVERED

為了過濾ACTION_NDEF_DISCOVERED intents, 聲明一個過濾器并且指定數(shù)據(jù)類型. 下面演示了如何過濾 ACTION_NDEF_DISCOVERED intents,它的MIME類型是text/plain:

<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain" />
</intent-filter>

下面演示了URI格式的過濾睛廊,過濾格式為 http://developer.android.com/index.html.

<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="http"
android:host="developer.android.com"
android:pathPrefix="/index.html" />
</intent-filter>

ACTION_TECH_DISCOVERED

如果你的程序想要過濾 ACTION_TECH_DISCOVERED intent, 你必須創(chuàng)建一個xml文件來描述你的程序支持的標(biāo)簽類型的規(guī)范形真,這個規(guī)范文件里包含你支持的最小的技術(shù)列表tech-list. 這個列表支持的技術(shù),將會和標(biāo)簽支持的技術(shù)進(jìn)行匹配超全。在程序運(yùn)行時咆霜,你可以獲得這些內(nèi)容,通過調(diào)用getTechList()方法嘶朱。

比如蛾坯,一個被掃描到的標(biāo)簽支持 MifareClassic, NdefFormatable, and NfcA, 你的程序里的支持列表里需要指明支持其中的三種,兩種疏遏,其中一種技術(shù)(或者可能都沒有) 為了使得你的程序被匹配到.

下面的示例定義了支持的所有的技術(shù). 你可以移除那些你不要的. 保存到 (可隨意命名) in the <project-root>/res/xml 文件夾.

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.IsoDep</tech>
        <tech>android.nfc.tech.NfcA</tech>
        <tech>android.nfc.tech.NfcB</tech>
        <tech>android.nfc.tech.NfcF</tech>
        <tech>android.nfc.tech.NfcV</tech>
        <tech>android.nfc.tech.Ndef</tech>
        <tech>android.nfc.tech.NdefFormatable</tech>
        <tech>android.nfc.tech.MifareClassic</tech>
        <tech>android.nfc.tech.MifareUltralight</tech>
    </tech-list>
</resources>

你可以指定多個 tech-list 集合. 每一個 tech-list 被認(rèn)為是獨(dú)立的, 并且你的程序?qū)黄ヅ涞揭粋€單一的 tech-list 偿衰, 它可以通過 getTechList()返回結(jié)果. 它為了技術(shù)匹配提供了 AND and OR 語義. 下面演示了一個標(biāo)簽匹配支持NfcA and Ndef 技術(shù) 或者 可被支持 NfcB and Ndef 技術(shù):

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.NfcA</tech>
        <tech>android.nfc.tech.Ndef</tech>
    </tech-list>
</resources>

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.NfcB</tech>
        <tech>android.nfc.tech.Ndef</tech>
    </tech-list>
</resources>

在你的 AndroidManifest.xml 文件, 指定你剛剛創(chuàng)建的資源文件的位置挂疆。 在<activity> 節(jié)點(diǎn)的 <meta-data> 節(jié)點(diǎn)下 ,下面是演示:

<activity>
...
<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" />
...
</activity>

想獲得更多標(biāo)簽的技術(shù)支持信息和 關(guān)于 ACTION_TECH_DISCOVERED intent,查閱Working with Supported Tag Technologies 在NFC高級篇.

ACTION_TAG_DISCOVERED

為了過濾 ACTION_TAG_DISCOVERED 使用下面的方式:

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

從 intents 中讀取信息

如果NFC intent啟動一個應(yīng)用, 你可以從這個 intent中獲得更多信息. 該Intents可以從標(biāo)簽里讀到到下列擴(kuò)展信息:

EXTRA_TAG (必選): 一個代表里讀取到的標(biāo)簽的 Tag 對象.
EXTRA_NDEF_MESSAGES (可選): 從標(biāo)簽的 NDEF messages 中讀取到的一個數(shù)據(jù)集合. 這個信息是強(qiáng)制的下翎。
{@link android.nfc.NfcAdapter#EXTRA_ID (可選): 標(biāo)簽的低級別的ID.
要獲得這些擴(kuò)展信息,請檢查如果您的程序是否被NFC intent啟動宝当,并確保一個標(biāo)簽被掃描视事,這時就可以從intent中讀取擴(kuò)展信息了。下面的示例檢查了一個ACTION_NDEF_DISCOVERED的intent庆揩,并嘗試從包含NDEF消息的intent中讀取擴(kuò)展信息俐东。.

public void onResume() {
    super.onResume();
    ...
    if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
        Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
        if (rawMsgs != null) {
            msgs = new NdefMessage[rawMsgs.length];
            for (int i = 0; i < rawMsgs.length; i++) {
                msgs[i] = (NdefMessage) rawMsgs[i];
            }
        }
    }
    //process the msgs array
}

另外,你也可以從intent得到一個tag對象订晌,它將包含有效載荷數(shù)據(jù)虏辫,并允許您列舉標(biāo)簽的技術(shù):

Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);

創(chuàng)建常見類型的NDEF紀(jì)錄

本節(jié)介紹如何創(chuàng)建一個通用類型的NDEF記錄,有助于你寫入到NFC標(biāo)簽锈拨,或發(fā)送數(shù)據(jù)與Android Beam砌庄。開始采用Android4.0(API級別14)中,createUri()方法可以幫助你自動創(chuàng)建URI類型的記錄奕枢。在Android4.1(API16級)娄昆,createExternal()和createMime()是可幫助您創(chuàng)建MIME類型和外部擴(kuò)展類型NDEF記錄。使用這些輔助方法使得在手動創(chuàng)建NDEF記錄時盡可能避免錯誤缝彬。

本節(jié)還介紹了如何創(chuàng)建相應(yīng)的intent filter的記錄萌焰。下面這些NDEF記錄示例必須在你的NDEF消息里的第一條NDEF記錄內(nèi),在你寫入標(biāo)簽或者Android Beam時.

TNF_ABSOLUTE_URI

注意: 我們推薦你使用 RTD_URI 類型來代替 TNF_ABSOLUTE_URI, 因為它效率更高.

你可以創(chuàng)建一個 TNF_ABSOLUTE_URI NDEF 記錄谷浅,按照下面的方式:

NdefRecord uriRecord = new NdefRecord(
    NdefRecord.TNF_ABSOLUTE_URI ,
    "http://developer.android.com/index.html".getBytes(Charset.forName("US-ASCII")),
    new byte[0], new byte[0]);

過濾上面這個NDEF記錄的intent filter這么寫:

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="http"
        android:host="developer.android.com"
        android:pathPrefix="/index.html" />
</intent-filter>

TNF_MIME_MEDIA

你可以創(chuàng)建一個 TNF_MIME_MEDIA NDEF 記錄扒俯, 按下面的方式.

使用 createMime() 方法:

NdefRecord mimeRecord = NdefRecord.createMime("application/vnd.com.example.android.beam",
    "Beam me up, Android".getBytes(Charset.forName("US-ASCII")));

手動方式創(chuàng)建 NdefRecord :

NdefRecord mimeRecord = new NdefRecord(
NdefRecord.TNF_MIME_MEDIA ,
"application/vnd.com.example.android.beam".getBytes(Charset.forName("US-ASCII")),
new byte[0], "Beam me up, Android!".getBytes(Charset.forName("US-ASCII")));

過濾上面這個NDEF記錄的intent filter這么寫:

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimeType="application/vnd.com.example.android.beam" />
</intent-filter>

TNF_WELL_KNOWN with RTD_TEXT

你可以創(chuàng)建一個 TNF_WELL_KNOWN NDEF 記錄,按照下面的方式:

public NdefRecord createTextRecord(String payload, Locale locale, boolean encodeInUtf8) {
    byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII"));
    Charset utfEncoding = encodeInUtf8 ? Charset.forName("UTF-8") : Charset.forName("UTF-16");
    byte[] textBytes = payload.getBytes(utfEncoding);
    int utfBit = encodeInUtf8 ? 0 : (1 << 7);
    char status = (char) (utfBit + langBytes.length);
    byte[] data = new byte[1 + langBytes.length + textBytes.length];
    data[0] = (byte) status;
    System.arraycopy(langBytes, 0, data, 1, langBytes.length);
    System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length);
    NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN,
    NdefRecord.RTD_TEXT, new byte[0], data);
    return record;
}

過濾上面這個NDEF記錄的intent filter這么寫:

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimeType="text/plain" />
</intent-filter>

TNF_WELL_KNOWN with RTD_URI

你可以創(chuàng)建一個 TNF_WELL_KNOWN NDEF 記錄一疯,按照下面的方式:

使用 createUri(String) 方法:

NdefRecord rtdUriRecord1 = NdefRecord.createUri("http://example.com");

使用 createUri(Uri) 方法:

Uri uri = new Uri("http://example.com");
NdefRecord rtdUriRecord2 = NdefRecord.createUri(uri);

手動創(chuàng)建 NdefRecord :

byte[] uriField = "example.com".getBytes(Charset.forName("US-ASCII"));
byte[] payload = new byte[uriField.length + 1];              //add 1 for the URI Prefix
byte payload[0] = 0x01;                                      //prefixes http://www. to the URI
System.arraycopy(uriField, 0, payload, 1, uriField.length);  //appends URI to payload
NdefRecord rtdUriRecord = new NdefRecord(
    NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, new byte[0], payload);

過濾上面這個NDEF記錄的intent filter這么寫:

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="http"
        android:host="example.com"
        android:pathPrefix="" />
</intent-filter>

TNF_EXTERNAL_TYPE

你可以創(chuàng)建一個 TNF_EXTERNAL_TYPE NDEF 記錄撼玄,按照下面的方式:

使用 createExternal() 方法:

byte[] payload; //assign to your data
String domain = "com.example"; //usually your app's package name
String type = "externalType";
NdefRecord extRecord = NdefRecord.createExternal(domain, type, payload);

手動創(chuàng)建 NdefRecord :

byte[] payload;
...
NdefRecord extRecord = new NdefRecord(
    NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType", new byte[0], payload);

The intent filter for the previous NDEF records would look like this:

<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.example:externalType"/>
</intent-filter>

使用 TNF_EXTERNAL_TYPE 類型是一個更普遍的標(biāo)簽,可以更好的支持android和非android設(shè)備.

注意: 關(guān)于TNF_EXTERNAL_TYPE 的URNs 有典型格式:urn:nfc:ext:example.com:externalType, 然而 NFC Forum RTD 規(guī)范聲明: urn:nfc:ext: 部分必須從NDEF記錄里被省略. 那么违施,所有你需要提供的內(nèi)容是一個域 (比如example.com ) 和類型(比如externalType)互纯,并用冒號分割它們. 當(dāng)標(biāo)簽分發(fā)系統(tǒng)分發(fā)一個TNF_EXTERNAL_TYPE的標(biāo)簽時, Android 轉(zhuǎn)換urn:nfc:ext:example.com:externalType 格式的URN 成vnd.android.nfc://ext/example.com:externalType 格式的URI, 它就是上面的intent過濾器里聲明的演示的樣子.

Android 應(yīng)用程序記錄 (AAR)

Android在Android4.0(API等級14)推出一個Android應(yīng)用程序記錄(AAR)磕蒲,AAR提供了更強(qiáng)的確定性留潦,在您的應(yīng)用程序因為一個NFC標(biāo)簽被掃描時而啟動時。 AAR具有嵌入在NDEF記錄內(nèi)的應(yīng)用程序的包名辣往。您可以添加一個AAR到您的任何NDEF記錄NDEF消息內(nèi)兔院,因為Android搜索整個NDEF消息內(nèi)的所有AAR。如果它發(fā)現(xiàn)AAR站削,根據(jù)里面的AAR的包名稱來啟動應(yīng)用程序坊萝。如果設(shè)備上不存在該應(yīng)用程序,谷歌PLAY(譯者注:應(yīng)用程序市場)將被啟動并導(dǎo)向去下載該應(yīng)用程序。.

如果你要防止其他應(yīng)用程序過濾功能可能相同的意圖和潛在的處理您已經(jīng)部署的特定標(biāo)簽十偶,AARs是有用的菩鲜。因為包名AARS只支持在應(yīng)用程序級別過濾,而不是在通過使用intetnt過濾器的Activity級別惦积。如果你想處理一個Activity的intent接校,請使用intent filter。

如果一個標(biāo)簽包含一個AAR狮崩,標(biāo)簽分發(fā)系統(tǒng)將會以下方式工作:.

嘗試按常規(guī)的方式使用intent filter過濾器來啟動一個activity. 如果該應(yīng)用匹配了過濾器的規(guī)則蛛勉,同時又匹配了AAR的規(guī)則,那么啟動該activity.
如果匹配了過濾器的規(guī)則睦柴,但是未匹配AAR規(guī)則诽凌;如果一個intent引發(fā)多個activity的啟動(譯者注:會彈出提示用戶手動選擇);或者沒有任何一個activity過濾到一個intent坦敌;那么將按照AAR程序規(guī)范執(zhí)行.
如果沒有應(yīng)用程序可以被AAR啟動侣诵,那么將被引導(dǎo)到Google Play 以下載AAR指向的應(yīng)用程序。
注意: 你可以使用 前端分發(fā)系統(tǒng)( foreground dispatch system)來覆蓋AARs和標(biāo)簽分發(fā)系統(tǒng)的處理方式, 它允許一個前端正在運(yùn)行的頂層活動的activity 擁有更高的優(yōu)先權(quán)來處理發(fā)現(xiàn)到的標(biāo)簽. 使用這種方法時恬试,必須要在前端頂層主動的覆蓋AARs和標(biāo)簽分發(fā)系統(tǒng)的行為.

如果你仍然想過濾那些不包含AAR的標(biāo)簽窝趣,你可以按常規(guī)形式聲明一個intent filter. 在你的應(yīng)用程序?qū)ζ渌话珹AR的標(biāo)簽有興趣時,這將很有用. 比如训柴, 或許你想保證你的程序 處理 你不熟的私有標(biāo)簽 as well as 第三方部署的常規(guī)標(biāo)簽. 保持關(guān)注 Android 4.0 及以后的設(shè)備規(guī)范, 這樣當(dāng)部署時, 你非逞剖妫可能想去使用一個組合AARs 和 MIME 類型的/URIs 為了支持更寬廣范圍的設(shè)備. 另外,當(dāng)你部署 NFC 標(biāo)簽, 考慮一下如果寫入你的NFC標(biāo)簽舍得可以支持更多的設(shè)備(Android設(shè)備和其他設(shè)備). 你可以這樣做幻馁,像定義相對唯一的 MIME 類型 或URI 的方式來使得更容易區(qū)分.

Android 提供了一個簡單的 API去創(chuàng)建一個AAR, createApplicationRecord(). 你所做的就是在任何位置嵌入一個AAR 到你的Ndef消息內(nèi). 你不需要使用你的ndef消息的第一條記錄, 除非你僅僅只有一條記錄. 這是因為 Android 系統(tǒng) 檢測 Ndef消息的第一條記錄來決定 MIME 類型 或者 標(biāo)簽的URI, 它常常用于為應(yīng)用程序過濾intent時創(chuàng)建一個intent. 下面的代碼演示了如果創(chuàng)建AAR:

NdefMessage msg = new NdefMessage(
        new NdefRecord[] {
            ...,
            NdefRecord.createApplicationRecord("com.example.android.beam")}

Beaming NDEF消息到其他設(shè)備

Android Beam可以支持兩款A(yù)ndroid設(shè)備之間的簡單的點(diǎn)到點(diǎn)數(shù)據(jù)交換洗鸵。想要進(jìn)行Android Beam數(shù)據(jù)到另一臺設(shè)備中的那臺的設(shè)備的應(yīng)用程序必須是在前臺程序(譯者注:活動的),而且接收該數(shù)據(jù)的設(shè)備不能被鎖定仗嗦。當(dāng)準(zhǔn)備進(jìn)行Android Beam的設(shè)備有足夠接近的接觸到準(zhǔn)備接收的設(shè)備時膘滨,Android Beam設(shè)備顯示“點(diǎn)擊屏幕開始Beam”的窗體。然后稀拐,用戶可以選擇是否Beam消息到接收設(shè)備內(nèi)火邓。.

注意: 前端 NDEF 推送 在API level 10后 是可用的, 它提供了和 Android Beam.類似的功能。那些 APIs 目前已經(jīng)被棄用, 但是在舊的設(shè)備仍然是可用的. 閱讀 enableForegroundNdefPush() 可以獲得更多信息.

你可以在你的程序里啟用 Android Beam 德撬,使用下面兩個方法中的任何一個:

setNdefPushMessage(): 接受一個 NdefMessage铲咨,作為準(zhǔn)備進(jìn)行beam的消息. 當(dāng)兩個設(shè)備非常接近時,自動的進(jìn)行beams 消息 .
setNdefPushMessageCallback(): 接受一個回調(diào)方法蜓洪,該回調(diào)包含了一個 createNdefMessage()方法纤勒, 該回調(diào)在一個設(shè)備在可以beam 的時候被調(diào)用. 這個回調(diào)方法允許你僅僅在需要的時候創(chuàng)建Ndef消息.
一個Activity 一次只能推送一個NDEF消息,所以如果兩者都設(shè)置了隆檀,setNdefPushMessageCallback()的優(yōu)先級超過setNdefPushMessage()摇天。使用Android Beam粹湃,必須滿足以下一般原則:
進(jìn)行beaming數(shù)據(jù)的 activity 必須在前端. 兩個設(shè)備的屏幕都不能被鎖定.
你必須封裝你準(zhǔn)備 beaming的數(shù)據(jù)到一個 NdefMessage 消息對象.
在NFC的裝置,接收的無線傳輸數(shù)據(jù)必須支持com.android.npp的NDEF的推送協(xié)議或NFC論壇的SNEP(簡單的NDEF交換協(xié)議)泉坐。該com.android.npp協(xié)議需要API 9級的Android 2.3設(shè)備为鳄,直到 API 13級Android 3.2的設(shè)備。 com.android.npp和SNEP都是必需的API14級(安卓4.0)和更高版本坚冀。
注意: 如果你的activity 啟用了 Android Beam 并且當(dāng)前處于前端活動的, 標(biāo)準(zhǔn)的標(biāo)簽分發(fā)系統(tǒng)將不可使用. 然而, 如果你的activity 仍然啟用的前端分發(fā)系統(tǒng)(foreground dispatching), 這是它仍然能匹配標(biāo)簽济赎,在你i的標(biāo)簽分發(fā)系統(tǒng)里匹配intent filters設(shè)置.

啟用 Android Beam 的方法:

創(chuàng)建一個 NdefMessage,這個消息里包含了你準(zhǔn)備推送到其他設(shè)備的 NdefRecord .
在你的activity的onCreate()方法中记某,調(diào)用 setNdefPushMessage() 傳入?yún)?shù)是一個 NdefMessage 或 者調(diào)用 setNdefPushMessageCallback 傳入一個 NfcAdapter.CreateNdefMessageCallback 對象. 這些方法都至少需要一個你要啟用Android Beam的activity,連同其他激活的activity的可選列表构捡。
在一般情況下液南,當(dāng)兩個設(shè)備都在溝通的范圍,如果僅僅推送相同的NDEF消息,您通常使用setNdefPushMessage()。當(dāng)您的應(yīng)用程序關(guān)心當(dāng)前應(yīng)用程序的上下文信息(目前情況)虐呻,或者希望根據(jù)用戶在做什么來推動一個NDEF消息藻三,您可以使用 setNdefPushMessageCallback。

下面的示例顯示了如何在一個簡單的activity的onCreate()方法中調(diào)用NfcAdapter.CreateNdefMessageCallback(完整的示例見AndroidBeamDemo)透揣。這個例子也可以幫助您創(chuàng)建一個MIME記錄:

package com.example.android.beam;

import android.app.Activity;
import android.content.Intent;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.NfcAdapter.CreateNdefMessageCallback;
import android.nfc.NfcEvent;
import android.os.Bundle;
import android.os.Parcelable;
import android.widget.TextView;
import android.widget.Toast;
import java.nio.charset.Charset;


public class Beam extends Activity implements CreateNdefMessageCallback {
    NfcAdapter mNfcAdapter;
    TextView textView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        TextView textView = (TextView) findViewById(R.id.textView);
        // Check for available NFC Adapter
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        if (mNfcAdapter == null) {
            Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show();
            finish();
            return;
        }
        // Register callback
        mNfcAdapter.setNdefPushMessageCallback(this, this);
    }

    @Override
    public NdefMessage createNdefMessage(NfcEvent event) {
        String text = ("Beam me up, Android!\n\n" +
                "Beam Time: " + System.currentTimeMillis());
        NdefMessage msg = new NdefMessage(
                new NdefRecord[] { createMime(
                        "application/vnd.com.example.android.beam", text.getBytes())
         /**
          * The Android Application Record (AAR) is commented out. When a device
          * receives a push with an AAR in it, the application specified in the AAR
          * is guaranteed to run. The AAR overrides the tag dispatch system.
          * You can add it back in to guarantee that this
          * activity starts when receiving a beamed message. For now, this code
          * uses the tag dispatch system.
          */
          //,NdefRecord.createApplicationRecord("com.example.android.beam")
        });
        return msg;
    }

    @Override
    public void onResume() {
        super.onResume();
        // Check to see that the Activity started due to an Android Beam
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
            processIntent(getIntent());
        }
    }

    @Override
    public void onNewIntent(Intent intent) {
        // onResume gets called after this to handle the intent
        setIntent(intent);
    }

    /**
     * Parses the NDEF Message from the intent and prints to the TextView
     */
    void processIntent(Intent intent) {
        textView = (TextView) findViewById(R.id.textView);
        Parcelable[] rawMsgs = intent.getParcelableArrayExtra(
                NfcAdapter.EXTRA_NDEF_MESSAGES);
        // only one message sent during the beam
        NdefMessage msg = (NdefMessage) rawMsgs[0];
        // record 0 contains the MIME type, record 1 is the AAR, if present
        textView.setText(new String(msg.getRecords()[0].getPayload()));
    }
}

注意,這段代碼注釋掉AAR,您也可以刪除它若未。如果您啟用了AAR,在AAR指定的應(yīng)用程序總是能接收Android Beam消息倾鲫。如果應(yīng)用程序是不存在的粗合,Google Play會啟動下載該應(yīng)用程序。因此乌昔,Android4.0或更高版本的設(shè)備下隙疚,如果使用的AAR,下面的意圖過濾器不是技術(shù)上必須的:

<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="application/vnd.com.example.android.beam"/>
</intent-filter>

有了這個意圖過濾器磕道,現(xiàn)在當(dāng)它掃描一個NFC標(biāo)簽供屉,或接收到一個AAR的類型com.example.android.beam的android beam消息時,或當(dāng)收到一個包含了 MIME 記錄application/vnd.com.example.android.beam.記錄的NDEF格式的消息溺蕉,現(xiàn)在的com.example.android.beam應(yīng)用程序?qū)⒖梢员粏恿尕ぁ!?/p>

即使AARS保證了應(yīng)用程序被啟動或下載焙贷,仍然建議使用意圖過濾器撵割。因為它可以讓你啟動您選擇的應(yīng)用程序中的Activity,而不是總是啟動一個AAR指定的包內(nèi)的主Activity辙芍。AARs沒有Activity級別水平的粒度啡彬。同時羹与,由于一些Android系統(tǒng)的設(shè)備不支持AARS,以防萬一庶灿,你也應(yīng)該嵌入的標(biāo)識信息在你的NDEF消息的第一條記錄里纵搁,并且使用過濾器過濾它。關(guān)于如何創(chuàng)建記錄的更多信息往踢,請參閱Creating Common Types of NDEF records 腾誉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市峻呕,隨后出現(xiàn)的幾起案子利职,更是在濱河造成了極大的恐慌,老刑警劉巖瘦癌,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猪贪,死亡現(xiàn)場離奇詭異,居然都是意外死亡讯私,警方通過查閱死者的電腦和手機(jī)热押,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來斤寇,“玉大人桶癣,你說我怎么就攤上這事∧锼” “怎么了牙寞?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長致盟。 經(jīng)常有香客問我碎税,道長,這世上最難降的妖魔是什么馏锡? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任雷蹂,我火速辦了婚禮,結(jié)果婚禮上杯道,老公的妹妹穿的比我還像新娘匪煌。我一直安慰自己,他們只是感情好党巾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布萎庭。 她就那樣靜靜地躺著,像睡著了一般齿拂。 火紅的嫁衣襯著肌膚如雪驳规。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天署海,我揣著相機(jī)與錄音吗购,去河邊找鬼医男。 笑死,一個胖子當(dāng)著我的面吹牛捻勉,可吹牛的內(nèi)容都是我干的镀梭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼踱启,長吁一口氣:“原來是場噩夢啊……” “哼报账!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起埠偿,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤透罢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后冠蒋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體琐凭,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年浊服,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胚吁。...
    茶點(diǎn)故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡牙躺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出腕扶,到底是詐尸還是另有隱情孽拷,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布半抱,位于F島的核電站脓恕,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏窿侈。R本人自食惡果不足惜炼幔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望史简。 院中可真熱鬧乃秀,春花似錦、人聲如沸圆兵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽殉农。三九已至刀脏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間超凳,已是汗流浹背愈污。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工耀态, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人钙畔。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓茫陆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親擎析。 傳聞我的和親對象是個殘疾皇子簿盅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評論 2 355

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