存儲(chǔ)優(yōu)化

Android存儲(chǔ)基礎(chǔ)

1、Android 分區(qū)

分區(qū)簡(jiǎn)單來說就是將設(shè)備中的存儲(chǔ)劃分為一些互不重疊的部分真友,每個(gè)部分都可以單獨(dú)格式化黄痪,用作不同的目的。這樣系統(tǒng)可以靈活的針對(duì)單獨(dú)的分區(qū)進(jìn)行不同的操作盔然,而不影響其他分區(qū)的數(shù)據(jù)桅打。


2、Android存儲(chǔ)安全

  • 權(quán)限控制
    Android的每個(gè)應(yīng)用都在自己的沙盒內(nèi)運(yùn)行愈案,在Android4.3之前采用了標(biāo)準(zhǔn)的Linux保護(hù)機(jī)制油额,通過為每個(gè)應(yīng)用創(chuàng)建獨(dú)一無二的Linux UID來定義。在Android4.3引入了SELinux(Security Enhance Linux)機(jī)制進(jìn)一步定義Android應(yīng)用沙盒的邊界刻帚。它的作用在于即使進(jìn)程有root權(quán)限也不能為所欲為,必須要在專門的安全策略配置文件中賦予權(quán)限涩嚣。
  • 數(shù)據(jù)加密
    除了權(quán)限控制崇众,數(shù)據(jù)加密也是保護(hù)數(shù)據(jù)的不錯(cuò)選擇。Android有兩種設(shè)備加密方法:全盤加密和文件加密航厚。全盤加密是在Android4.4中引入的顷歌,并在Android5.0中默認(rèn)打開的。它會(huì)將/data分區(qū)的用戶數(shù)據(jù)操作加密/解密幔睬,對(duì)性能會(huì)有一定的影響眯漩,但是新版本的芯片都會(huì)在硬件中直接提供支持。基于文件系統(tǒng)的加密赦抖,如果設(shè)備被解鎖了舱卡,加密就沒有用了,所以Android7.0增加了基于文件的加密队萤。在這種模式下轮锥,將會(huì)給每個(gè)文件分配一個(gè)必須用戶的passcode推導(dǎo)出來的密鑰,特定的文件被屏幕鎖屏之后要尔,直到用戶下一次解鎖屏幕期間都不能訪問舍杜。

常見數(shù)據(jù)存儲(chǔ)方法

1、關(guān)鍵因素

選擇存儲(chǔ)方法時(shí)一般會(huì)考慮一下因素:


2赵辕、存儲(chǔ)選項(xiàng)

  • SharePreferences
  • ContentProvider
  • 文件
  • 數(shù)據(jù)庫

1)SharePreferences的使用

  • 跨進(jìn)程不安全
  • 加載緩慢既绩。SharePreferences文件的加載使用了異步線程,而且加載線程沒有設(shè)置線程優(yōu)先級(jí)还惠,如果這個(gè)時(shí)候主線程讀取數(shù)據(jù)就需要等待文件加載線程的結(jié)束饲握。這就導(dǎo)致出現(xiàn)主線程等待低優(yōu)先級(jí)線程鎖的問題。
  • 全量寫入
    無論調(diào)用commit()還是apply()吸重,即使我們只改動(dòng)其中一個(gè)條目互拾,都會(huì)把整個(gè)內(nèi)容全部寫到文件。而且多次寫入嚎幸,SP也沒有合并寫入的操作颜矿。
  • 卡頓
    由于執(zhí)行了異步落盤的apply機(jī)制,在崩潰或者其他異常情況可能會(huì)導(dǎo)致數(shù)據(jù)丟失嫉晶。所以當(dāng)應(yīng)用收到系統(tǒng)廣播骑疆,或者被調(diào)用OnPause等一些時(shí)機(jī),系統(tǒng)會(huì)強(qiáng)制把所有的SharePreferences對(duì)象數(shù)據(jù)落地到磁盤替废,如果沒有落地完成箍铭,這是主線程會(huì)一直被阻塞,這樣非常容易造成卡頓椎镣,很值A(chǔ)NR诈火。
    系統(tǒng)提供的SharePreferences的應(yīng)用場(chǎng)景是用來存儲(chǔ)一些非常簡(jiǎn)單、輕量的數(shù)據(jù)状答,不適合復(fù)雜和大量的數(shù)據(jù)冷守。推薦微信開源MMKV。

2)ContentProvider的使用

  • 啟動(dòng)性能
    ContentProvider的生命周期默認(rèn)在Application onCreate()之前惊科,而且在主線程創(chuàng)建的拍摇,我們自定義的ContentProvider類的構(gòu)造函數(shù)、靜態(tài)代碼馆截,onCreate函數(shù)盡量不要做耗時(shí)的操作充活,會(huì)拖慢啟動(dòng)速度蜂莉。
  • 穩(wěn)定性
    ContentProvider在跨進(jìn)程數(shù)據(jù)傳遞時(shí),利用了Android的Binder和匿名共享內(nèi)存機(jī)制混卵。就是通過Binder傳遞CursorWindow對(duì)象內(nèi)部的匿名共享內(nèi)存的文件描述符映穗,這樣在跨進(jìn)程傳輸中,結(jié)果數(shù)據(jù)并不需要跨進(jìn)程傳輸淮菠,而在不同進(jìn)程中通過傳輸匿名共享文件描述符來操作同一塊匿名內(nèi)存男公,這樣來實(shí)現(xiàn)不同進(jìn)程訪問相同數(shù)據(jù)的目的。



    基于mmap的匿名共享內(nèi)存機(jī)制也是有代價(jià)的合陵,當(dāng)傳輸?shù)臄?shù)量非常小的時(shí)候枢赔,并不一定劃算。所以ContentProvider提供了一種call函數(shù)拥知,他會(huì)直接通過Binder來傳輸數(shù)據(jù)踏拜。Android的Binder傳輸是有大小限制的,一般來說是1-2MB低剔,ContentProvider接口的調(diào)用參數(shù)和call函數(shù)調(diào)用并沒有使用匿名共享機(jī)制速梗,當(dāng)插入很多數(shù)據(jù),那么就會(huì)出現(xiàn)個(gè)很大的插入數(shù)組襟齿,那么這個(gè)操作可能會(huì)出現(xiàn)數(shù)據(jù)超大異常姻锁。

  • 安全性
    雖然ContentProvider提供了很好的安全機(jī)制,但如果ContentProvider支持exported猜欺,當(dāng)支持SQL時(shí)要注意SQL注入問題位隶,另外如果傳入的參數(shù)是文件路徑,要校驗(yàn)合法性开皿,不然應(yīng)用的私有數(shù)據(jù)可能被別人拿到涧黄。


對(duì)象序列化

對(duì)象序列化就是把一個(gè)Object對(duì)象所有的信息表示成一個(gè)字節(jié)序列,包括class信息赋荆、繼承關(guān)系信息笋妥、訪問權(quán)限、變量類型以及數(shù)值信息等窄潭。

1春宣、Serializable

Serializable是Java原生的序列化機(jī)制,廣泛地在Android 中使用嫉你,可以通過Bundle傳遞Serializable的序列化數(shù)據(jù)信认。
Serializable的原理是通過ObjectInputStream和ObjectOutputStream來實(shí)現(xiàn)的。整個(gè)序列化過程大量地使用了反射和臨時(shí)變量均抽,整個(gè)計(jì)算非常復(fù)雜,序列化性能比較差其掂。

  • Serializable支持替代默認(rèn)流程的序列化油挥,它會(huì)先反射判斷是否存在我們自己實(shí)現(xiàn)的序列化方法writeObject和反序列化方法readObject。通過這兩個(gè)方法可以對(duì)某些字段做些特殊修改,也可以實(shí)現(xiàn)序列化的加密功能深寥。
  • writeReplace和readReplace這個(gè)兩個(gè)方法代理序列化對(duì)象攘乒,可以實(shí)現(xiàn)自定義返回的序列化實(shí)例。我們可以通過它們實(shí)現(xiàn)對(duì)象序列化的版本兼容惋鹅。
    Serializable注意事項(xiàng):
  • 不被序列化的字段则酝。類的static變量以及被聲明為transient字段,默認(rèn)的序列化機(jī)制都會(huì)忽略該字段闰集,不會(huì)進(jìn)行序列化存儲(chǔ)沽讹。當(dāng)然我們可以通過writeObject和readObject實(shí)現(xiàn)自定義序列化存儲(chǔ)方案。
  • serialVersionUID武鲁。在類實(shí)現(xiàn)了Serializable接口后爽雄,我們需要添加一個(gè)Serial Version ID,它相當(dāng)于版本號(hào)沐鼠,這個(gè)ID可以顯示聲明也可以讓編譯器自己計(jì)算挚瘟。通常建議顯示聲明會(huì)更加穩(wěn)妥,因?yàn)殡[式聲明假如發(fā)生了一點(diǎn)點(diǎn)變化饲梭,進(jìn)行反序列化都會(huì)由于serialVersionUID的變化而引發(fā)InvalidClassException異常乘盖。
  • 構(gòu)造方法。 Serializable的反序列化默認(rèn)是不會(huì)執(zhí)行構(gòu)造函數(shù)的憔涉,它會(huì)根據(jù)數(shù)據(jù)流中對(duì)Object的描述信息創(chuàng)建對(duì)象订框,如果一些邏輯依賴構(gòu)造函數(shù),就可能出現(xiàn)問題监氢。

2布蔗、Parcelable

Parcelable是專門針對(duì)Android設(shè)計(jì)的輕量且高效地對(duì)象序列化和反序列化實(shí)現(xiàn)方案。Parcelable的實(shí)現(xiàn)核心都在Parcel.cpp浪腐,它只會(huì)在內(nèi)存中進(jìn)行序列化操作纵揍,并不會(huì)將數(shù)據(jù)存儲(chǔ)到磁盤里。也可以通過Parcel.cpp的marshall接口獲取byte數(shù)組议街,然后存在文件中從而實(shí)現(xiàn)永久保存泽谨。
Parcelable注意事項(xiàng):
Parcelable在使用的時(shí)候需要手動(dòng)添加自定義代碼,使用起來比Serializable要麻煩一些特漩,正是因?yàn)槿绱薖arcelable才不需要使用反射的方法去實(shí)現(xiàn)吧雹,提高了性能。

  • 系統(tǒng)版本兼容性涂身。由于Parcelable設(shè)計(jì)的本意是在內(nèi)存中使用的雄卷,無法保證所有Android 版本的Parcel.cpp的實(shí)現(xiàn)都完全一致,可能會(huì)存在兼容問題蛤售。
  • 數(shù)據(jù)前后兼容性丁鹉。Parcelable并沒有版本管理的設(shè)計(jì)妒潭,如果出現(xiàn)版本升級(jí),寫入的順序及字段類型兼容需要格外注意揣钦,一般來說雳灾,如果需要持久存儲(chǔ)的話,還是不得不選擇性能較差的Serializable方案冯凹。

3谎亩、Serial

Teitter開源的高性能序列化方案Serial。從實(shí)現(xiàn)原理來看Serial就像是把Parcelable和Serializable的有點(diǎn)集合在一起的方案宇姚。

  • 由于沒有使用反射匈庭,比傳統(tǒng)方案更加高效
  • 開發(fā)者對(duì)于序列化過程的控制較強(qiáng),可以定義哪些Object空凸、Field需要序列化
  • 有很強(qiáng)的debug能力嚎花,可以調(diào)試序列化的過程
  • 有很強(qiáng)的版本管理能力,可以通過版本號(hào)和OptionalFieldException做兼容

數(shù)據(jù)的序列化

1呀洲、JSON

JSON是一種輕量級(jí)的數(shù)據(jù)交換格式紊选,它被廣泛的使用在網(wǎng)絡(luò)傳輸中,很多應(yīng)用與服務(wù)端的通訊業(yè)采用JSON進(jìn)行通訊道逗。
JSON的優(yōu)勢(shì):

  • 相比對(duì)象序列化方案兵罢,速度更快,體積更小滓窍。
  • 相比二進(jìn)制的序列化方案卖词,結(jié)果可讀,易于排查問題吏夯。
  • 使用方便此蜈,支持跨平臺(tái)、跨語言噪生,支持嵌套使用裆赵。


    各平臺(tái)JSON方案對(duì)比

2、Protocol Buffers

Protocol Buffers是Google開源的跨語言編碼協(xié)議跺嗽,適用于數(shù)據(jù)量非常大战授,對(duì)性能要求高的方案。

  • 性能
    使用二進(jìn)制編碼壓縮桨嫁,相比于JSON體積更小植兰,編解碼速度更快
  • 兼容性
    跨語言支持和前后兼容性都不錯(cuò),也支持基本類型的自動(dòng)轉(zhuǎn)換璃吧,但不支持繼承和引用類型楣导。
  • 使用成本
    開發(fā)成本很高,需要定義.proto文件畜挨,并用工具生成對(duì)應(yīng)的輔助類爷辙。輔助類特有一些序列化的輔助方法彬坏,所有要序列化的對(duì)象,都需要先轉(zhuǎn)化為輔助類的對(duì)象膝晾,這讓序列化代碼跟業(yè)務(wù)代碼大量耦合,是侵入性較強(qiáng)的一種方式务冕。


    序列化方案對(duì)比

存儲(chǔ)監(jiān)控

1血当、性能監(jiān)控

  • 正確性
  • 時(shí)間開銷
  • 空間開銷

2、ROM監(jiān)控

除了某個(gè)存儲(chǔ)模塊的監(jiān)控禀忆,我們也需要對(duì)應(yīng)用整體的ROM空間做詳細(xì)監(jiān)控臊旭。ROM監(jiān)控的兩個(gè)核心指標(biāo)是文件總大小與總文件數(shù)。

SQLite優(yōu)化

1箩退、基礎(chǔ)知識(shí)

1)ORM

ORM(Object Relational Mapping)即關(guān)系對(duì)象映射离熏,用面向?qū)ο蟮母拍畎褦?shù)據(jù)庫中表和對(duì)象關(guān)聯(lián)起來,讓我們可以不用關(guān)心數(shù)據(jù)庫底層實(shí)現(xiàn)戴涝。Android中常見ORM框架有g(shù)reenDAO和Google官方的Room滋戳,ORM框架使用簡(jiǎn)單,但是以犧牲部分執(zhí)行效率為代價(jià)的啥刻。

2)進(jìn)程與線程并發(fā)

并發(fā)問題會(huì)導(dǎo)致SQLiteDatabaseLockedException奸鸯,SQLite并發(fā)有兩個(gè)維度,一個(gè)多進(jìn)程并發(fā)可帽,另一個(gè)是多線程并發(fā)娄涩。

多進(jìn)程并發(fā)

SQLite默認(rèn)支持多進(jìn)程并發(fā),它通過文件鎖來控制多進(jìn)程的并發(fā)映跟。SQLite鎖的粒度并沒有非常細(xì)蓄拣,它針對(duì)的是整個(gè)DB文件,內(nèi)部有5個(gè)狀態(tài)努隙。多進(jìn)程可以同時(shí)獲取SHARED鎖來讀取數(shù)據(jù)球恤,但是只有一個(gè)進(jìn)程可以獲取EXCLUSIVE鎖來寫數(shù)據(jù)庫,在EXCLUSIVE模式下剃法,數(shù)據(jù)庫連接在斷開之前都不會(huì)釋放SQLite文件的鎖碎捺,從而避免不必要的沖突,提高數(shù)據(jù)庫訪問速度贷洲。

多線程并發(fā)

SQLite默認(rèn)支持多洗那成并發(fā)模式收厨,更多進(jìn)程的鎖機(jī)制一樣,為了實(shí)現(xiàn)簡(jiǎn)單优构,SQLite鎖的粒度都是文件級(jí)別诵叁,并沒有實(shí)現(xiàn)表級(jí)甚至行級(jí)的鎖。同一個(gè)句柄同一時(shí)間只有一個(gè)線程在操作钦椭,這個(gè)時(shí)候我們需要打開連接池Connection Pool拧额。多線程可以同時(shí)讀取數(shù)據(jù)庫數(shù)據(jù)碑诉,但是寫數(shù)據(jù)庫依然是互斥的。SQLite提供了Busy Retry的方案侥锦,即發(fā)生阻塞時(shí)會(huì)觸發(fā)Busy Retry进栽,此時(shí)可以讓線程休眠一段時(shí)間后,重新嘗試操作恭垦。如果出現(xiàn)多個(gè)寫并發(fā)的情況快毛,依然有可能出現(xiàn)SQLiteDatabaseLockedException,這個(gè)時(shí)候應(yīng)用可以捕獲這個(gè)異常番挺,然后等待一段時(shí)間后再嘗試唠帝。

3)查詢優(yōu)化

索引優(yōu)化

正確使用索引大部分情況下可以大大降低查詢速度,索引的建立總體來說有一定的原則:

  • 建立正確的索引玄柏, 選擇最優(yōu)的索引創(chuàng)建
  • 單例索引襟衰、多列索引與符合索引的選擇
    索引要綜合數(shù)據(jù)表中不同查詢與排序語句一起考慮,如果查詢結(jié)果集過大粪摘,還是希望通過復(fù)合索引直接在索引表返回查詢結(jié)果瀑晒。
  • 索引字段的選擇
    整型類型索引效率會(huì)遠(yuǎn)高于字符串索引,而對(duì)于主鍵SQLite會(huì)默認(rèn)幫我們建立索引赶熟,所以主鍵盡量不要用復(fù)雜字段瑰妄。

頁大小與緩存大小

對(duì)于SQLite的DB文件來說,頁(page)是最小的存儲(chǔ)單位映砖,每個(gè)表對(duì)應(yīng)的數(shù)據(jù)在整個(gè)DB文件都是通過一個(gè)一個(gè)頁存儲(chǔ)间坐,屬于同一個(gè)表不同的頁以B樹(B-tree)的方式組織索引,每一個(gè)表都是一個(gè)B樹邑退。



跟文件系統(tǒng)的頁緩存一樣竹宋,SQLite會(huì)將讀過的頁緩存起來,用來加快下一次讀取速度地技。頁大小默認(rèn)1024Byte蜈七,緩存大小默認(rèn)1000頁。每個(gè)頁永遠(yuǎn)只會(huì)存放一個(gè)表或一組索引的數(shù)據(jù)莫矗,即不可能同一個(gè)頁存放多個(gè)表或索引數(shù)據(jù)飒硅。增大頁的大小不能不斷提升性能,在拐點(diǎn)之后會(huì)起反作用作谚,建議選擇4KB作為默認(rèn)的page size以獲得更好的性能三娩。

其他優(yōu)化

  • 慎用select *
  • 正確使用事務(wù)
    -預(yù)編譯與參數(shù)綁定,緩存被編譯后的SQL語句
  • 對(duì)于blob或超大的text列妹懒,可能會(huì)超出一個(gè)頁的大小雀监,導(dǎo)致出現(xiàn)超大頁,建議將這些列單獨(dú)拆表,或者放到表字段的后面
  • 定期整理或清理無用或可刪除的數(shù)據(jù)会前。

2好乐、SQLite其他特性

1)損害與修復(fù)

2)加密與安全

數(shù)據(jù)庫的安全主要有兩個(gè)方面,一個(gè)是防注入瓦宜,一個(gè)是加密蔚万。防注入可以通過靜態(tài)安全掃描的方式,而加密一般會(huì)使用SQLClipher支持临庇。
SQLite的加解密都是以頁為單位笛坦,默認(rèn)使用AES算法加密,加解密的耗時(shí)跟選用的密鑰長(zhǎng)度有關(guān)苔巨。

3)全文搜索

3、SQLite的監(jiān)控

1)本地測(cè)試

2)耗時(shí)監(jiān)控

3)智能監(jiān)控

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末废离,一起剝皮案震驚了整個(gè)濱河市侄泽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蜻韭,老刑警劉巖悼尾,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異肖方,居然都是意外死亡闺魏,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門俯画,熙熙樓的掌柜王于貴愁眉苦臉地迎上來析桥,“玉大人,你說我怎么就攤上這事艰垂∨菡蹋” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵猜憎,是天一觀的道長(zhǎng)娩怎。 經(jīng)常有香客問我,道長(zhǎng)胰柑,這世上最難降的妖魔是什么截亦? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮柬讨,結(jié)果婚禮上崩瓤,老公的妹妹穿的比我還像新娘。我一直安慰自己姐浮,他們只是感情好谷遂,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著卖鲤,像睡著了一般肾扰。 火紅的嫁衣襯著肌膚如雪畴嘶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天集晚,我揣著相機(jī)與錄音窗悯,去河邊找鬼。 笑死偷拔,一個(gè)胖子當(dāng)著我的面吹牛蒋院,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播莲绰,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼欺旧,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了蛤签?” 一聲冷哼從身側(cè)響起辞友,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎震肮,沒想到半個(gè)月后称龙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡戳晌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年鲫尊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沦偎。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡疫向,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出扛施,到底是詐尸還是另有隱情鸿捧,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布疙渣,位于F島的核電站匙奴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏妄荔。R本人自食惡果不足惜泼菌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望啦租。 院中可真熱鬧哗伯,春花似錦、人聲如沸篷角。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至虐块,卻和暖如春俩滥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背贺奠。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工霜旧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人儡率。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓挂据,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親儿普。 傳聞我的和親對(duì)象是個(gè)殘疾皇子崎逃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

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