Android 重學系列 SurfaceFlinger的概述

前言

本文將會作為開啟SurfaceFlinger的系列第一篇文章陵像。然而SurfaceFlinger幾乎貫通了整個Android領域中所有的知識闸溃。從HAL硬件抽象層到Framework層逆粹,從CPU繪制到OpenGL等硬件繪制裤唠。

為了讓整個系列的書寫更有邏輯性。這一次我將一反常態(tài)鞠值,先把整個架構的設計思想概述寫出來媚创,作為后面的系列文章的指導彤恶。本文之后都會將SurfaceFlinger稱為SF。

遇到什么疑問声离,可以來本文下討論:http://www.reibang.com/p/c954bcceb22a

正文

SF的渲染第一定律:

SF是整個Android系統(tǒng)渲染的核心進程芒炼。所有應用的渲染邏輯最終都會來到SF中進行處理焕议,最終會把處理后的圖像數(shù)據(jù)交給CPU或者GPU進行繪制。

姑且讓我們先把這句話當作Android渲染系統(tǒng)的第一定律世囊。SF在整個Android系統(tǒng)中窿祥,并非擔當渲染的角色,而是作為圖元拋射機一樣必逆,把所有應用進程傳遞過來的圖元數(shù)據(jù)加工處理后恳谎,交給CPU和GPU做真正的繪制靠胜。

SF的渲染第二定律:

在每一個應用中都以Surface作為一個圖元傳遞單元浪漠,向SF這個服務端傳遞圖元數(shù)據(jù)。

這是Android渲染體系的第二定律址愿。把這兩個規(guī)律組合起來就是如下一個簡單示意圖。

SF交互設計圖.png

SF的渲染第三定律:

SF是以生產(chǎn)者以及消費者為核心設計思想俱饿,把每一個應用進程作為生產(chǎn)者生產(chǎn)圖元保存到SF的圖元隊列中塌忽,SF則作為消費者依照一定的規(guī)則把生產(chǎn)者存放到SF中的隊列一一處理土居。

用圖表示就如下:


圖元消費核心原理.png

SF體系渲染的第四定律:

為了能夠跨進程的傳輸大容量的圖元數(shù)據(jù)棉圈,使用了匿名共享內(nèi)存內(nèi)存作為工具把圖元數(shù)據(jù)都輸送到SF中處理眷蜓。

眾所周知吁系,我們需要從應用進程跨進程把圖元數(shù)據(jù)傳輸?shù)絊F進程中處理汽纤,就需要跨進程通信蕴坪,能考慮的如socket這些由于本身效率以及數(shù)據(jù)拷貝了2份(從物理內(nèi)存頁層面上來看)背传,確實不是很好的選擇径玖。一個本身拷貝大量的數(shù)據(jù)就是一個疑問挺狰。那么就需要那些一次拷貝的進程間通信方式丰泊,首先能想到的當然是Binder瞳购,然而Binder進程間通信,特別是應用的通信數(shù)據(jù)總量只有1M不到的大小加上應用其他通信吞杭,勢必會出現(xiàn)不足的問題芽狗。

為了解決這個問題痒蓬,Android使用共享內(nèi)存攻晒,使用的是匿名共享內(nèi)存(Ashmem)鲁捏。匿名共享內(nèi)存也是一種拷貝一次的進程間通信方式给梅,其核心比起binder的復雜的mmap更加接近Linux的共享內(nèi)存的概念破喻。

Ashmem.png

SF體系渲染的第五定律:

SF底層有一個時間鐘在不斷的循環(huán)曹质,或從硬件中斷發(fā)出羽德,或從軟件模擬發(fā)出計時喚起宅静,每隔一段時間都會獲取SF中的圖元隊列通過CPU/GPU繪制在屏幕姨夹。

第五定律的誕生實際上很符合Android系統(tǒng)的設計情況磷账,除了需要Android應用有辦法通知SF需要渲染的模式逃糟,當然需要SF自己不斷的把圖元繪制到屏幕的行為的自己回調(diào)自己的行為绰咽,SF自己不斷的繪制在SF中的圖元數(shù)據(jù)取募。

SF Vsync.png

其中EventThread扮演一個極其重要的角色笑跛,在SF中設計大致如下:


EventThread.png

Vsync的介紹

這里面出現(xiàn)了一個新的名次VSync飞蹂,其實這就是我們玩游戲經(jīng)常說的垂直同步信號陈哑。我以前用渣電腦玩游戲的時候惊窖,經(jīng)常掉幀數(shù)卡的不行界酒,之后關閉了垂直信號后感覺好了點毁欣,讓我有一段時間以為這是個不好的東西凭疮。

這里就先介紹一下Android曾經(jīng)迭代為ui體驗更好上的努力执解,黃油計劃衰腌。黃油計劃故名思議就是為了讓系統(tǒng)的ui表現(xiàn)如黃油表面一樣順滑右蕊。為此誕生了兩個重要的概念Vsync以及Triple Buffer尤泽,即垂直信號和三重緩沖坯约。

雙緩沖的概念大家應該都熟悉闹丐,在OpenGL我已經(jīng)解釋過了卿拴,雙緩沖就是渲染第一幀的同時已經(jīng)在繪制第二幀的內(nèi)容堕花,等到第二幀繪制完畢后就顯示出來缘挽。這么做的好處很明顯壕曼,如果一幀畫完腮郊,才開始畫下一幀轧飞,勢必有一個計算的過程導致ui交互遲緩踪少。

雙緩沖.png

通過這種方式顯示前一幀的時候提前繪制好下一幀圖元,放在背后等待時機交換集漾,這樣就能從感官上流暢不少具篇。

這么做理想十分顯示驱显,但是怎么找到一個合適的時機進行交換前后兩幀這是一個問題埃疫?如果有人在想那就按照屏幕刷新頻率來栓霜,一般按照通用屏幕刷新60fps也就是約16ms刷新一次即可胳蛮。

理想是很豐滿仅炊,但是現(xiàn)實很骨干抚垄,這么做好像沒有問題督勺,我們深入考慮一下智哀,其實這個過程中有兩個變量屯吊,一個是繪圖速度盒卸,一個是顯示速度蔽介。就算是繪圖速度中也有分CPU和GPU的繪制速度虹蓄。

這里就沿用一下當年google在宣傳黃油計劃時候的示意圖薇组。讓我們先看看沒有緩沖正常運作的示意圖:


draw_vsync.png

最好的情況就是上圖宋光,在顯示第0幀的時候跃须,CPU/GPU合成繪制完成第1幀在16ms內(nèi)菇民,當vsync信號來了第练,就把第1幀交換到顯示屏顯示娇掏。

vsync是什么婴梧?玩游戲的時候經(jīng)橙洌看到垂直同步就是它。它的作用是通過屏幕硬件中斷來告訴系統(tǒng)應該什么時候刷新屏幕漱办。通過這樣的方式娩井,大致上16ms的發(fā)送一次中斷讓系統(tǒng)刷新洞辣。

但是很可能出現(xiàn)下面這種情況屋彪,CPU因為繁忙來不及仔粥,顯示完第一幀的時候,還沒空渲染第二幀华糖,就算SF接受到了Vsync的信號客叉,也只能拿出已經(jīng)渲染好的第一幀顯示在屏幕上兼搏。這樣就重復顯示了第一幀,Google開發(fā)團隊稱這種為jank吓著。


jank.png

能看到顯示第一幀因為第二幀沒準備好绑莺,只能重復顯示第一幀了。

再來看看帶著多重緩沖的的工作原理流程:


double_buffer.png

能看到此時就不是簡單的第一第二幀对扶,而是分為A緩沖,B緩沖络凿。能看到在正常情況下絮记,先顯示A緩沖的內(nèi)容怨愤,同時準備B緩沖篮愉,當一切正常的時候试躏,B緩沖應該在下一個vsync來之前準備好颠蕴,一旦vsync到來則顯示B緩沖犀被,A緩沖回到后臺繼續(xù)繪制。

那么這種方式一旦遇到jank會是怎么一個情況呢昌腰?


double_jank.png

如果是雙緩沖好像沒有問題遭商,但是一旦出現(xiàn)jank了之后,之后顯示屏就會不斷的出現(xiàn)jank祠汇。如果緩沖A在顯示可很,而B準備的時間超過16ms我抠,就會導致A緩沖區(qū)重復顯示,而B當b顯示的時候袜茧,A也很可能準備時間不足16ms導致無法繪制完成菜拓,只能重復顯示B緩沖的內(nèi)容。

這種方式更加的危險笛厦,為了解決這個問題纳鼎,Google引入三重緩沖。

當三重緩沖處理jank的原理流程圖:


triple_buffer.png

能看到為了避免后面連鎖式的錯誤喷橙,引入三重緩沖就為了讓空閑出來的等待時間啥么,能夠做更多的事情登舞。就如同雙緩沖遇到jank之后贰逾,一旦B緩沖CPU+GPU的時間超過了下一個vsync的時間,能夠發(fā)現(xiàn)其實CPU和GPU有一段時間都沒有事情做菠秒,光等待下一次Vsync的到來疙剑,才會導致整個系統(tǒng)后面的繪制出現(xiàn)連鎖式的出現(xiàn)jank。

而三緩沖的出現(xiàn)践叠,在重復顯示A緩沖區(qū)的時候言缤,CPU不會光等待而是會準備C緩沖區(qū)的圖元,之后就能把C緩沖區(qū)接上禁灼。這就是Google所說的三重緩沖區(qū)的來源管挟。

不過絕大多數(shù)情況都緩沖策略是由SF系統(tǒng)自己決定的,一般我們常說的雙緩沖弄捕,三緩沖指的就是這個僻孝。

實際上這種方式也可以用到音視頻的編寫優(yōu)化,里面常用的緩沖區(qū)設計和這里也有同工異曲的之妙守谓,但是沒有系統(tǒng)如此極致穿铆。如果閱讀過系統(tǒng)的videoView源碼就能看到NullPlayer本質(zhì)上就是借助Surface圖元緩沖區(qū)來達到極致的體驗,不過VideoView也有設計不合理的地方斋荞,之后研讀完Android的渲染體系荞雏,讓我們來分析分析這些源碼。

但是這一部分的知識平酿,不足以讓我們?nèi)ダ斫舛?.其實每一次Vsync從硬件/軟件過來的時候凤优,Dispsync都會嘗試著通知SF和app,這是完全沒有問題蜈彼,但是后面那個Phase相位又是什么東西筑辨?

其實這就是系統(tǒng)的設計的巧妙,我們?nèi)绻瑫r把信號通知同時告訴app和sf會導致什么結果柳刮?


無phase的沖突.png

如果此時app后返回了圖元挖垛,但是sf已經(jīng)執(zhí)行了刷新合成繪制行為(很有可能,因為app到sf傳輸圖元速度必定比sf自己通知自己慢)秉颗,此時就會導致類似jank的問題痢毒,導致下一個vsync還是顯示當前幀數(shù),因此需要如下一個時間差蚕甥,先通知app后通知sf哪替,如下圖:


sf和app的時間差.png

加上這個理解就能明白第五定律。關于第五點的討論菇怀,在Vsync同步信號原理有詳細討論凭舶。

小結

這五大定律是指導SF設計的核心思想晌块,從Android4.1一直到9.0都沒有太大的變化。只要抓住這五個核心思想帅霜,我們閱讀起SF的難度就會下降不少匆背。

那么SF的體系和我之前聊過的Skia有什么關系呢?又和頂層的View的繪制流程有什么關系呢身冀?

我們按照角色區(qū)分一下:

  1. framework面向開發(fā)者所有的View是便于開發(fā)的控件钝尸,里面僅僅只是提供了當前View各種屬性以及功能。
  2. 而Android底層的Skia是Android對于屏幕上的畫筆搂根,經(jīng)過View繪制流程的onDraw方法回調(diào)珍促,把需要繪制的東西通過Skia繪制成像素圖元保存起來
  3. SF則是最后接受Skia的繪制結果,最后繪制到屏幕上剩愧。

所以說猪叙,Skia是Android渲染核心這句話沒錯,但是最終還是需要Skia和系統(tǒng)所提供起來仁卷,才是一個Android完整渲染體系穴翩。

經(jīng)過這一層層的屏蔽,讓開發(fā)者不需要對Android底層的渲染體系有任何理解五督,也能繪制出不錯的效果藏否。

Android的渲染流程.png

最后會把繪制結果傳輸?shù)狡聊恢小?/p>

因此,本次計劃將會從底層核心充包,慢慢向上剖析副签,直到View的繪制流程,讓我們那徹底通讀整個android的渲染體系基矮。

計劃

本次計劃SurfaceFlinger的文章將會通過如下模塊一一解析(但是不代表一個模塊就只有一篇淆储,也不代表最終順序,僅僅代表你將會閱讀到什么內(nèi)容):

    1. 圖元核心傳輸工具家浇,匿名共享內(nèi)存ashmem驅動的核心原理,ashmem原理圖大致如下:


      ashmem.png

詳見匿名內(nèi)存ashmem源碼分析本砰。然而在Android高版本,已經(jīng)放棄了ashemem钢悲,改用ion驅動点额。ion的原理圖大致如下:

GraphicBuffer和ion.png

關于ion的分析,詳見ion驅動源碼淺析

ion實際上是生成DMA直接訪問內(nèi)存莺琳。原本ashmem的方式需要從GPU訪問到CPU再到內(nèi)存中的地址还棱。但是在這里就變成了GPU直接訪問修改DMA,CPU也能直接修改DMA惭等。這就是最大的變化珍手。

    1. 開機沒有Activity琳要,只能直接使用SF機制加上OpenGL es顯示開機動畫寡具,來看看從linux開機動畫到Android開機動畫 BootAnimation 。
      詳見系統(tǒng)啟動動畫稚补,原理圖大致如下:
      開機動畫啟動原理.jpg
    1. 理解應用進程如何和SF構建起聯(lián)系童叠。
      詳見Vsync同步信號原理。SF是通過一個名為Choreographer監(jiān)聽VSync進而得知繪制周期的孔厉。原理圖大致如下:
      VSync回調(diào)機制.jpg
    1. SF硬件抽象層hal的理解和運作拯钻,理解SF如何和底層HWC/fb驅動關聯(lián)起來帖努。

詳見SurfaceFlinger 的HAL層初始化
其核心數(shù)據(jù)結構如下:

HWC關鍵數(shù)據(jù)結構.jpg

底層硬件回調(diào)和SF之間的關聯(lián)原理圖如下:


ComposerCallback.png
    1. SF是如何連通DisplayManagerService[略,之后有機會進行補充]撰豺,只是簡單的通過SurfaceFlinger獲取屏幕信息放在Framework層管理。
    1. Android端在opengl es的核心原理拼余,看看Android對opengl es上做了什么封裝污桦。
      這個模塊分為兩部分解析:
      一個是正常的OpenGL es使用流程中,軟件模擬每一個關鍵步驟的工作原理是什么匙监,Android在其中進行了什么優(yōu)化凡橱。詳見OpenGL es上的封裝(上)
      其中有一個十分關鍵的數(shù)據(jù)結構,UML圖如下:
      紋理結構.png

一個紋理在OpenGL es中是如何合成繪制的亭姥,并且Android進行了本地紋理的優(yōu)化,詳見OpenGL es上的封裝(下)稼钩,整個OpenGL es的繪制原理如下:

OpenGL es紋理繪制過程.png

    1. 圖元是怎么通過hal層生產(chǎn)出圖元數(shù)據(jù);應用的圖元數(shù)據(jù)又是獲取到應用达罗,如何進入SurfaceFlinger的緩沖隊列坝撑。
      詳見GraphicBuffer的誕生。其中涉及了幾個重要的數(shù)據(jù)結構:
      GraphicBuffer生成體系.png

同時運行原理圖如下:


GraphicBuffer誕生到可使用.png
    1. 應用的圖元數(shù)據(jù)是如何消費的粮揉。
      詳見圖元的消費巡李,交換緩沖繪制參數(shù),本質(zhì)是取出一個GraphicBuffer存到緩沖隊列的時間和當前時間預計顯示最接近的一個扶认,渲染到屏幕中侨拦。同時把上一幀的GraphicBuffer放到空閑隊列中。

其中辐宾,我們需要記住下面這個SF中緩沖隊列設計的數(shù)據(jù)結構:


Layer與緩沖隊列的設計.png
    1. SF是如何通過HWC合成圖層狱从,如何合并各個Layer,輸出到opengles中處理叠纹。
      大致上可以分為如下如下7步驟:
      1.preComposition 預處理合成
      2.rebuildLayerStacks 重新構建Layer棧
      3.setUpHWComposer HWC的渲染或者準備
      這三步驟季研,我稱為繪制準備,詳見圖元的合成(上) 繪制的準備
      在繪制準備的過程中吊洼,最重要的是區(qū)分了如下幾種繪制模式训貌,已經(jīng)存儲相關的數(shù)據(jù)到HWC的Hal層中。
Composition的Layer的Type hasClientComposition hasDeviceComposition 渲染方式
HWC2::Composition::Client true - OpenGL es
HWC2::Composition::Device - true HWC
HWC2::Composition::SolidColor - true HWC
HWC2::Composition::Sideband - true HWC或者OpenGL es

4.doDebugFlashRegions 打開debug繪制模式
5.doTracing 跟蹤打印
6.doComposition 合成圖元
7.postComposition 圖元合成后的vysnc等收尾工作。

后面四個步驟递沪,我們只需要關注最后兩個步驟即可豺鼻。詳見圖元的合成(下)

整一套的從消費到合成的流程原理圖大致如下:


SF的圖元合成.png

在合成的過程中,分為HWC和OpenGL es兩種款慨,兩者負責的角色大致如下:


SF的圖元合成設計.png

當然儒飒,在Android渲染體系中,也不是只有一對生產(chǎn)者消費者模型:


SF所有生產(chǎn)消費者.png
    1. SF的Vsync原理檩奠,以及相位差計算原理
      整個VSync發(fā)送中有三種發(fā)送周期:硬件發(fā)送VSync周期桩了,軟件發(fā)送VSync周期,app處理VSync周期埠戳,sf處理VSync周期井誉。
      詳見Vsync同步信號原理

Android為了方便,會暫時把整個周期看成一個周期連續(xù)性的函數(shù)整胃,計算原理如下:


計算角度.png

其實就是獲取每一個采樣點相位颗圣,計算采樣點相位的平均值就是理想相位。同理屁使,周期也是計算采樣點的平均周期在岂,從而計算出一個合適的軟件發(fā)送VSync軸。

最后在軟件渲染的基礎上蛮寂,app的VSync和sf的VSync各自進行延時接受處理蔽午,避免出現(xiàn)定律的時序沖突,就是上面那一副藍色的圖酬蹋。

    1. SF的fence 同步柵工作原理
      詳見fence原理
      想要弄懂Fence及老,需要先了解GraphicBuffer的狀態(tài)變更:
      大致分為如下幾個狀態(tài):dequeue(出隊到應用中繪制),queue(入隊到SF緩沖區(qū)等待消費)除嘹,acquire(選擇渲染的GraphicBuffer)写半,free(消費完畢后等待dequeue)
      GraphicBuffer狀態(tài)流轉.png

Fence的狀態(tài)更簡單,有acquire尉咕,release叠蝇,retried狀態(tài)流轉大致如下:


fence轉化流程圖.png

retried是每一次繪制完都會合并在一個不用的Fence中進行記錄。

總結一句話年缎,F(xiàn)ence的acquire狀態(tài)其實是阻塞什么時候可以被消費悔捶,什么時候可以被渲染到屏幕;而Fence的release狀態(tài)則是控制什么時候可以出隊給應用進行繪制单芜,什么時候可以被映射到內(nèi)存蜕该。

只有理解這12點,才能說你了解SF了洲鸠,也不能說精通堂淡,畢竟你沒辦法盲敲出來馋缅。

等這12點全部理解通之后,會開啟Skia新的篇章绢淀,來聊聊Skia的工作原理以及源碼解析萤悴,最后我們會回歸本源,來聊聊View的繪制流程以及WMS皆的。

這個只是一個導讀覆履,在這12個知識點背后藏著不少的東西,希望一個總綱能讓人有一個總攬费薄,不至于迷失在源碼中硝全。

后話

作為每一個經(jīng)常和UI交互的工程師,有必要也必須要熟悉Android 的渲染原理楞抡,只有這樣才能讓我們寫出更加優(yōu)秀代碼伟众,特別是做音視頻的哥們,更加有必要閱讀這些代碼以及看看工業(yè)級別的Android的VideoView是如何設計的拌倍。其實我在學習一些關于音視頻資料的時候赂鲤,用ffmpeg編寫一個視頻播放器,發(fā)現(xiàn)其實那些demo還有很多地方可以優(yōu)化的柱恤,可以學習flutter如何工作的,如何依托自身平臺做進一步優(yōu)化找爱,而不是應該去做一個泛用的梗顺,還過得去的東西。

我會隨著進度不斷修改本文车摄,本文不是最終版本寺谤,會不斷的添加不少設計示意圖以及UML圖。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吮播,一起剝皮案震驚了整個濱河市变屁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌意狠,老刑警劉巖粟关,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異环戈,居然都是意外死亡闷板,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進店門院塞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遮晚,“玉大人,你說我怎么就攤上這事拦止∠厍玻” “怎么了糜颠?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長萧求。 經(jīng)常有香客問我括蝠,道長,這世上最難降的妖魔是什么饭聚? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任忌警,我火速辦了婚禮,結果婚禮上秒梳,老公的妹妹穿的比我還像新娘法绵。我一直安慰自己,他們只是感情好酪碘,可當我...
    茶點故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布朋譬。 她就那樣靜靜地躺著,像睡著了一般兴垦。 火紅的嫁衣襯著肌膚如雪徙赢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天探越,我揣著相機與錄音狡赐,去河邊找鬼。 笑死钦幔,一個胖子當著我的面吹牛枕屉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鲤氢,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼搀擂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了卷玉?” 一聲冷哼從身側響起哨颂,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎相种,沒想到半個月后威恼,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡蚂子,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年沃测,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片食茎。...
    茶點故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蒂破,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出别渔,到底是詐尸還是另有隱情附迷,我是刑警寧澤惧互,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站喇伯,受9級特大地震影響喊儡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜稻据,卻給世界環(huán)境...
    茶點故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一艾猜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧捻悯,春花似錦匆赃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至姓言,卻和暖如春瞬项,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背何荚。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工囱淋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人兽泣。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓绎橘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親唠倦。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,654評論 2 354

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