Android高手開發(fā)課之高質(zhì)量開發(fā) 學(xué)習(xí)筆記

image

極客課程地址

主要總結(jié) 用兩周的時(shí)間淺顯的過(guò)了一遍shwen大佬的高手開發(fā)課 高質(zhì)量開發(fā)章節(jié)內(nèi)容赏半,以便后續(xù)繼續(xù)學(xué)習(xí)使用

1. native崩潰

Android 崩潰分為 Java 崩潰Native 崩潰昼弟。

Java 崩潰 : 是在 Java 代碼中悬蔽,出現(xiàn)了未捕獲異常棒仍,導(dǎo)致程序異常退出。

Native 崩潰 : 一般都是因?yàn)樵?Native 代碼中訪問(wèn)非法地址抡蛙,也可能是地址對(duì)齊出現(xiàn)了問(wèn)題护昧,或者發(fā)生了程序主動(dòng) abort,這些都會(huì)產(chǎn)生相應(yīng)的 signal 信號(hào)粗截,導(dǎo)致程序異常退出捏卓。

崩潰捕捉流程


崩潰捕捉流程

崩潰捕捉難點(diǎn):

  1. 文件句柄泄漏,導(dǎo)致創(chuàng)建日志文件失敗 (提前申請(qǐng)文件句柄 fd 預(yù)留)
  2. 棧溢出了慈格,導(dǎo)致日志生成失敗 (signalstack)
  3. 堆的內(nèi)存都耗盡了,導(dǎo)致日志生成失敗 (Breakpad)
  4. 堆破壞或二次崩潰導(dǎo)致日志生成失敗 (Breakpad)

使用Breakpad 捕捉Native異常 :鏈接 Github lib 工具類

崩潰分析

崩潰現(xiàn)場(chǎng):

  1. 崩潰信息 (進(jìn)程名 線程名 崩潰堆棧 )
  2. 系統(tǒng)信息 (logcat 機(jī)型 系統(tǒng) 廠商 cpu等信息)
  3. 內(nèi)存信息 (使用內(nèi)存 剩余內(nèi)存)

崩潰嘗試解決:

  1. 查找可能存在的問(wèn)題 (根據(jù)logcat 日志 等信息定位)
  2. 嘗試規(guī)避 (try catch 遥金、避免使用某一個(gè)api)
  3. Hook 解決 (例如 Android 7.0 Toast失效)

2. 內(nèi)存優(yōu)化

內(nèi)存造成的問(wèn)題:

  1. 異常 (OOM浴捆、內(nèi)存分配失敗)
  2. 卡頓 (java內(nèi)存不足引發(fā)的頻繁GC gc的時(shí)候程序會(huì)有卡頓 、系統(tǒng)負(fù)載過(guò)高)

舉例子:
Android 3.0到8.0 關(guān)于Bitmap優(yōu)化的演變稿械,從開始的數(shù)據(jù)放native bitmap放java內(nèi)存选泻,到都放Java內(nèi)存,演變到8.0則都放在Native層美莫。

如何將Bitmap定義在Native層

eg:


// 步驟一:申請(qǐng)一張空的 Native Bitmap
Bitmap nativeBitmap = nativeCreateBitmap(dstWidth, dstHeight, nativeConfig, 22);

// 步驟二:申請(qǐng)一張普通的 Java Bitmap
Bitmap srcBitmap = BitmapFactory.decodeResource(res, id);

// 步驟三:使用 Java Bitmap 將內(nèi)容繪制到 Native Bitmap 中
mNativeCanvas.setBitmap(nativeBitmap);
mNativeCanvas.drawBitmap(srcBitmap, mSrcRect, mDstRect, mPaint);

// 步驟四:釋放 Java Bitmap 內(nèi)存
srcBitmap.recycle();
srcBitmap = null页眯;

存在的問(wèn)題:1.版本兼容問(wèn)題 2頻繁申請(qǐng)釋放Java Bitmap

測(cè)量?jī)?nèi)存

adb shell dumpsys meminfo <package_name|pid> [-d]

脫離 Android Studio,實(shí)現(xiàn)一個(gè)自定義的“Allocation Tracker”

原理:
項(xiàng)目使用了 inline hook 來(lái)攔截內(nèi)存對(duì)象分配時(shí)候的 RecordAllocation 函數(shù)厢呵,通過(guò)攔截該接口可以快速獲取到當(dāng)時(shí)分配對(duì)象的類名和分配的內(nèi)存大小窝撵。
在初始化的時(shí)候我們?cè)O(shè)置了一個(gè)分配對(duì)象數(shù)量的最大值,如果從 start 開始對(duì)象分配數(shù)量超過(guò)最大值就會(huì)觸發(fā)內(nèi)存 dump襟铭,然后清空 alloc 對(duì)象列表碌奉,重新計(jì)算。

內(nèi)存優(yōu)化

  1. 設(shè)備分級(jí) (可參考device-year-class 對(duì)設(shè)備進(jìn)行分級(jí) 寒砖,根據(jù)設(shè)備環(huán)境分配內(nèi)存)
  2. Bitmap優(yōu)化 (從Java內(nèi)存遷移到Native赐劣、圖片尺寸/質(zhì)量壓縮、超容器大圖處理哩都、重復(fù)圖片優(yōu)化魁兼、Bitmap內(nèi)存及時(shí)回收)
  3. 緩存管理 (統(tǒng)一緩存管理機(jī)制 負(fù)責(zé)各個(gè)模塊大小)
  4. 進(jìn)程優(yōu)化 (一個(gè)空進(jìn)程也會(huì)占用很多內(nèi)存漠嵌,避免多余進(jìn)程的使用)
  5. 內(nèi)存泄漏 (LeakCanary 檢測(cè)對(duì)象生命周期是否異常咐汞、游標(biāo)釋放盖呼、強(qiáng)引用持有對(duì)象回收、Handler 弱引用 碉考、WebView單獨(dú)進(jìn)程維護(hù))
  6. 資源優(yōu)化 (壓縮圖片塌计、Svg圖片類型使用、so庫(kù)優(yōu)化侯谁、語(yǔ)言文件優(yōu)化等)

啟動(dòng)優(yōu)化

app啟動(dòng)四個(gè)狀態(tài)

  1. 預(yù)覽窗口顯示 :系統(tǒng)在拉起app進(jìn)程之前锌仅,會(huì)先根據(jù)app的 Theme 屬性創(chuàng)建預(yù)覽窗口。當(dāng)然如果我們禁用預(yù)覽窗口或者將預(yù)覽窗口指定為透明墙贱,用戶在這段時(shí)間依然看到的是桌面热芹。
  2. 閃屏顯示: 在app進(jìn)程和閃屏窗口頁(yè)面創(chuàng)建完畢,并且完成一系列 inflate view惨撇、onmeasure伊脓、onlayout 等,可以看到歡迎界面了
  3. 主頁(yè)顯示: 在完成主窗口創(chuàng)建和頁(yè)面顯示的準(zhǔn)備工作后魁衙,用戶可以看到app的主界面报腔,對(duì)應(yīng) onCreate;
  4. 界面可操作性 : 走到 onResume 方法剖淀,用戶即可進(jìn)行操作

優(yōu)化方式

  1. 閃屏優(yōu)化 (歡迎界面 設(shè)置默認(rèn)背景)
  2. 業(yè)務(wù)梳理 (避免Applcation集體初始化纯蛾,可以維護(hù)進(jìn)程池用于不重要模塊的懶加載)
  3. 線程優(yōu)化 (避免多個(gè)任務(wù)等待執(zhí)行,通過(guò)有向無(wú)環(huán)圖 設(shè)置加載順序 減少 CPU 調(diào)度帶來(lái)的波動(dòng)纵隔,讓應(yīng)用的啟動(dòng)時(shí)間更加穩(wěn)定)
  4. 安裝包不壓縮 (啟動(dòng)過(guò)程需要的文件翻诉,我們可以指定在安裝包中不壓縮,這樣也會(huì)加快啟動(dòng)速度捌刮,但帶來(lái)的影響是安裝包體積增大)

更黑科技點(diǎn)的優(yōu)化方式

  1. I/O 優(yōu)化 (磁盤I/O優(yōu)化 sp文件過(guò)大碰煌,db過(guò)大 都需要優(yōu)化,啟動(dòng)過(guò)程不建議出現(xiàn)網(wǎng)絡(luò) I/O)
  2. 數(shù)據(jù)/類/資源 重排 (ReDex 調(diào)整dex排序 绅作、支付寶資源重排優(yōu)化

I/O優(yōu)化

CPU 和內(nèi)存相比磁盤是高速設(shè)備芦圾,整個(gè)流程的瓶頸在于磁盤 I/O 的性能

文件系統(tǒng)

image

  1. 虛擬文件系統(tǒng)(VFS)。它主要用于實(shí)現(xiàn)屏蔽具體的文件系統(tǒng)俄认,為應(yīng)用程序的操作提供一個(gè)統(tǒng)一的接口堕扶。
  2. 文件系統(tǒng)(File System)。ext4梭依、F2FS 都是具體文件系統(tǒng)實(shí)現(xiàn)稍算,文件元數(shù)據(jù)如何組織、目錄和索引結(jié)構(gòu)如何設(shè)計(jì)役拴、怎么分配和清理數(shù)據(jù)糊探,
  3. 頁(yè)緩存(Page Cache)。在讀文件的時(shí)候會(huì),先看它是不是已經(jīng)在 Page Cache 中科平,如果命中就不會(huì)去讀取磁盤褥紫。

磁盤

image

在一些低端機(jī)上面,大量跟 I/O 相關(guān)的卡頓原因:

  1. 內(nèi)存不足瞪慧。當(dāng)手機(jī)內(nèi)存不足的時(shí)候髓考,系統(tǒng)會(huì)回收 Page Cache 和 Buffer Cache 的內(nèi)存,大部分的寫操作會(huì)直接落盤弃酌,導(dǎo)致性能低下
  2. 寫入放大氨菇。內(nèi)存重復(fù)寫入需要先進(jìn)行擦除操作,但這個(gè)擦除操作的基本單元是 block 塊妓湘,一個(gè) page 頁(yè)的寫入操作將會(huì)引起整個(gè)塊數(shù)據(jù)的遷移查蓉,這就是典型的寫入放大現(xiàn)象
  3. 由于低端機(jī)的 CPU 和閃存的性能相對(duì)也較差,在高負(fù)載的情況下容易出現(xiàn)瓶頸

系統(tǒng)為了緩解磁盤碎片問(wèn)題榜贴,可以引入 fstrim/TRIM 機(jī)制豌研,在鎖屏、充電等一些時(shí)機(jī)會(huì)觸發(fā)磁盤碎片整理唬党。

mmap
它是通過(guò)把文件映射到進(jìn)程的地址空間鹃共,帶來(lái)的好處:

  1. 減少系統(tǒng)調(diào)用。我們只需要一次 mmap() 系統(tǒng)調(diào)用驶拱,后續(xù)所有的調(diào)用像操作內(nèi)存一樣及汉,而不會(huì)出現(xiàn)大量的 read/write 系統(tǒng)調(diào)用
  2. 減少數(shù)據(jù)拷貝。普通的 read() 調(diào)用屯烦,數(shù)據(jù)需要經(jīng)過(guò)兩次拷貝;而 mmap 只需要從磁盤拷貝一次就可以了房铭,并且由于做過(guò)內(nèi)存映射驻龟,也不需要再拷貝回用戶空間。

缺點(diǎn):

  1. 虛擬內(nèi)存增大
  2. 磁盤延遲

I/O跟蹤

  1. Java Hook (找到切入點(diǎn) 缸匪。缺點(diǎn):性能差 沒(méi)辦法監(jiān)控Native代碼 每個(gè)版本源碼可能不同 都需要兼容)
  2. Native Hook (暫時(shí)了解)

I/O優(yōu)化

  1. 對(duì)大文件使用 mmap 或者 NIO 方式 (MappedByteBuffer就是 Java NIO 中的 mmap 封裝)
  2. Buffer 復(fù)用 (Okio開源庫(kù)翁狐,它內(nèi)部的 ByteString 和 Buffer 通過(guò)重用等技巧,很大程度上減少 CPU 和內(nèi)存的消耗)
  3. 存儲(chǔ)結(jié)構(gòu)和算法的優(yōu)化

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

SharedPreferences 存在的問(wèn)題

  1. 跨進(jìn)程不安全 (由于沒(méi)有使用跨進(jìn)程的鎖凌蔬,就算使用MODE_MULTI_PROCESS 也會(huì)丟失露懒。)
  2. 加載緩慢。(sp 文件的加載使用了異步線程砂心,而且加載線程并沒(méi)有設(shè)置線程優(yōu)先級(jí)懈词。)
  3. 全量寫入 (無(wú)論是調(diào)用 commit() 還是 apply(),即使我們只改動(dòng)其中的一個(gè)條目辩诞,都會(huì)把整個(gè)內(nèi)容全部寫到文件坎弯。而且即使我們多次寫入同一個(gè)文件,SP 也沒(méi)有將多次修改合并為一次)
  4. 卡頓 (由于提供了異步落盤的 apply 機(jī)制,在崩潰或者其他異常情況可能會(huì)導(dǎo)致數(shù)據(jù)丟失抠忘。當(dāng)應(yīng)用收到系統(tǒng)廣播撩炊,或者被調(diào)用 onPause 等一些時(shí)機(jī),系統(tǒng)會(huì)強(qiáng)制把所有的 sp 對(duì)象數(shù)據(jù)落地到磁盤崎脉。如果沒(méi)有落地完成拧咳,這時(shí)候主線程會(huì)被一直阻塞)

MMKV 的實(shí)現(xiàn)原理,里面有一些非常不錯(cuò)的思路囚灼。例如利用文件鎖保證跨進(jìn)程的安全骆膝、使用 mmap 保證數(shù)據(jù)不會(huì)丟失、選用性能和存儲(chǔ)空間更好的 Protocol Buffer 代替 XML啦撮、支持增量更新等

ContentProvider

ContentProvider 的生命周期默認(rèn)在 Application onCreate() 之前谭网,而且都是在主線程創(chuàng)建的,盡量不做過(guò)多操作赃春,通過(guò) ContentProvider 也可以獲取到數(shù)據(jù)愉择,比如系統(tǒng)提供的 日歷、通訊錄等等

序列化

  1. Serializable

??Java原生序列化機(jī)制 缺點(diǎn):大量的反射和臨時(shí)變量织中,而且在序列化對(duì)象的時(shí)候锥涕,不僅會(huì)序列化當(dāng)前對(duì)象本身,還會(huì)遞歸序列化對(duì)象引用的其他對(duì)象狭吼。

??我們能做的優(yōu)化:1. writeObject 和 readObject 方法层坠,Serializable在反射的時(shí)候會(huì)先檢查是否自己實(shí)現(xiàn)了 writeObj 和 readObj . 2 writeReplace 和 readResolve 方法,可以自定義序列化返回結(jié)果,

read/writeResolve  read/writeObject 順序 區(qū)別
// 序列化
E/test:SerializableTestData writeReplace
E/test:SerializableTestData writeObject

// 反序列化
E/test:SerializableTestData readObject
E/test:SerializableTestData readResolve

Serializable 注意: 類的 static 變量以及被聲明為 transient 的字段刁笙,默認(rèn)的序列化機(jī)制都會(huì)忽略該字段破花。 開發(fā)中盡量設(shè)置 serialVersionUID

??2. Parcelable

在寫入和讀取的時(shí)候都需要手動(dòng)添加自定義代碼,使用起來(lái)相比 Serializable 會(huì)復(fù)雜很多疲吸。但Parcelable 不需要采用反射的方式去實(shí)現(xiàn)序列化和反序列化

??3. Json

優(yōu)點(diǎn):速度更快座每,體積更小,結(jié)果可讀便于排查問(wèn)題摘悴。使用方便峭梳,支持跨平臺(tái)、跨語(yǔ)言蹂喻,支持嵌套引用

??4. Protocol Buffers

由于使用二進(jìn)制格式葱椭,相比Json 體積更小,傳輸更快口四。

??5. SQLite

??SQLite本身是支持多線程 多進(jìn)程操作的孵运,通過(guò)文件鎖來(lái)控制多進(jìn)程的并發(fā)。SQLite 鎖的粒度精確到db蔓彩,沒(méi)有到某一行掐松。

??多進(jìn)程可以同時(shí)獲取 SHARED 鎖來(lái)讀取數(shù)據(jù)踱侣,但是只有一個(gè)進(jìn)程可以獲取 EXCLUSIVE 鎖來(lái)寫數(shù)據(jù)庫(kù)。

??多線程情況下可以使用連接池大磺,或者使用WAL模式抡句,將讀和寫完全的并發(fā)執(zhí)行。
??通過(guò)正確的建立索引杠愧,可以提升 SQLite 的查詢速度待榔。通過(guò)調(diào)整默認(rèn)的頁(yè)大小和緩存大小,可以提升 SQLite 的整體性能流济。

索引參考文章
wcdb github

網(wǎng)絡(luò)優(yōu)化

網(wǎng)絡(luò)優(yōu)化大頭在網(wǎng)絡(luò)請(qǐng)求:

  1. DNS 解析 (通過(guò) DNS 服務(wù)器锐锣,拿到對(duì)應(yīng)域名的 IP 地址 優(yōu)化點(diǎn):HTTPDNS 百度DNS優(yōu)化)
  2. 創(chuàng)建連接 (服務(wù)器建立連接,這里包括 TCP 三次握手绳瘟、TLS 密鑰協(xié)商等工作 優(yōu)化點(diǎn):連接復(fù)用雕憔,通過(guò)將連接存放連接池和Http2.0的多路復(fù)用 或者Http1.0的 keep-alive 都對(duì)連接有優(yōu)化作用)
  3. 發(fā)送 / 接收數(shù)據(jù) (如何根據(jù)網(wǎng)絡(luò)狀況將帶寬利用好,怎么樣快速地偵測(cè)到網(wǎng)絡(luò)延時(shí)糖声,在弱網(wǎng)絡(luò)下如何調(diào)整包大小 優(yōu)化點(diǎn):Http2.0 頭部壓縮 和 請(qǐng)求Gzip壓縮 算法壓縮等方式)
  4. 關(guān)閉連接 (從主動(dòng)關(guān)閉和被動(dòng)關(guān)閉兩種情況優(yōu)化)

UI優(yōu)化

關(guān)于屏幕適配可以參考限寬適配今日頭條UI適配

優(yōu)化點(diǎn):

  1. 盡量使用硬件加速 (Gpu可以分擔(dān)cpu渲染工作量提升速度)
  2. View的異步加載 /重用 (子線程Looper替換為UI線程Looper 用完記得還原 斤彼。重用可以參考 ListView 和 RecycleView復(fù)用原理)
  3. measure/layout 優(yōu)化 (減少UI層級(jí) 推薦使用ConstraintLayout 代替Rl 和Ll ,避免重復(fù)設(shè)置背景)
  4. Compose 學(xué)習(xí)和使用 (聲明式UI布局 讓界面更加簡(jiǎn)單 減少xml解析時(shí)間 蘸泻,ui的未來(lái))

安裝包優(yōu)化

  1. 代碼方面 可以使用 ProGuard對(duì)代碼進(jìn)行壓縮簡(jiǎn)化琉苇,但是對(duì)混淆規(guī)則 要避免重復(fù)keep 。更高級(jí)的牽扯到dex的壓縮 和 Native Lib的分割和合并 參考:redex github Lib合并參考
  2. 資源壓縮可以參考 AndResGuard AndResGuard美團(tuán)講解 和 騰訊的 matrix思路(官方提供: Lint 和 shrinkResources)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末悦施,一起剝皮案震驚了整個(gè)濱河市并扇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌抡诞,老刑警劉巖穷蛹,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異昼汗,居然都是意外死亡肴熏,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門乔遮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人取刃,你說(shuō)我怎么就攤上這事蹋肮。” “怎么了璧疗?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵坯辩,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我崩侠,道長(zhǎng)漆魔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮改抡,結(jié)果婚禮上矢炼,老公的妹妹穿的比我還像新娘。我一直安慰自己阿纤,他們只是感情好句灌,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著欠拾,像睡著了一般胰锌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上藐窄,一...
    開封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天资昧,我揣著相機(jī)與錄音,去河邊找鬼荆忍。 笑死格带,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的东揣。 我是一名探鬼主播践惑,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼嘶卧!你這毒婦竟也來(lái)了尔觉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤芥吟,失蹤者是張志新(化名)和其女友劉穎侦铜,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钟鸵,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡钉稍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了棺耍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贡未。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蒙袍,靈堂內(nèi)的尸體忽然破棺而出俊卤,到底是詐尸還是另有隱情,我是刑警寧澤害幅,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布消恍,位于F島的核電站,受9級(jí)特大地震影響以现,放射性物質(zhì)發(fā)生泄漏狠怨。R本人自食惡果不足惜约啊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望佣赖。 院中可真熱鬧恰矩,春花似錦、人聲如沸茵汰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蹂午。三九已至栏豺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間豆胸,已是汗流浹背奥洼。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留晚胡,地道東北人灵奖。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像估盘,于是被迫代替她去往敵國(guó)和親瓷患。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

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