安卓之圖片加載基礎(chǔ)(一)

本章節(jié)討論的是安卓圖片加載的基礎(chǔ)部分宰衙,主要依據(jù)谷歌官方培訓(xùn)教程中的代碼和方法,使用比較廣泛的圖片加載框架,很多都是要依據(jù)安卓的基礎(chǔ)方法沥阱,在此基礎(chǔ)上進(jìn)行封裝,如果直接看封裝過(guò)得框架伊群,以來(lái)難度會(huì)有點(diǎn)大考杉,二來(lái)在基礎(chǔ)方法不是太了解的情況下看大的完整框架會(huì)有些耗費(fèi)時(shí)間策精,所以還是讓我們來(lái)一起分析一下如何用安卓官方給出的基礎(chǔ)方法來(lái)封裝一個(gè)簡(jiǎn)單圖像加載框架。

因?yàn)楣雀柙谥袊?guó)建立了針對(duì)開(kāi)發(fā)者的開(kāi)發(fā)文檔服務(wù)器崇棠,各位安卓開(kāi)發(fā)人員想要學(xué)習(xí)谷歌官方的安卓文檔已經(jīng)不需要再翻墻了咽袜。雖然部分文檔還是英文版的,但是最起碼我們可以快速方便的打開(kāi)了枕稀,地址:谷歌官方安卓開(kāi)發(fā)文檔

以下相關(guān)源代碼和理論分析主要來(lái)自于:安卓圖像之高效顯示Bitmap

一询刹、高效加載大圖:首先,在應(yīng)用開(kāi)發(fā)中在顯示圖片時(shí)萎坷,往往很多原圖是比較大的凹联,而手機(jī)設(shè)備是不需要在內(nèi)存中加載一個(gè)原圖大小的大圖的,這就需要加載一個(gè)符合手機(jī)需要大小的圖片哆档,以此來(lái)減少內(nèi)存加載圖片時(shí)過(guò)多的內(nèi)存消耗蔽挠。接下來(lái)用一段代碼進(jìn)入分析:

BitmapFactory.Options options = new BitmapFactory.Options();

options.inJustDecodeBounds = true;

BitmapFactory.decodeResource(getResources(), R.id.myimage, options);

int imageHeight = options.outHeight;

int imageWidth = options.outWidth;

String imageType = options.outMimeType;

BitmapFactory提供了一些解碼(decode)的方法(decodeByteArray(), decodeFile(), decodeResource()等),用來(lái)從不同的資源中創(chuàng)建一個(gè)Bitmap瓜浸。這些方法在構(gòu)造位圖的時(shí)候會(huì)嘗試分配內(nèi)存澳淑,因此OutOfMemory的異常往往在此時(shí)發(fā)生。

該段代碼使用了BitmapFactory.Options來(lái)獲取圖片的寬高和類(lèi)型插佛,而當(dāng)options.inJustDecodeBounds設(shè)置為true時(shí)杠巡,內(nèi)存是不會(huì)加載該圖片的,即返回一個(gè)為null的bitmap引用朗涩,但是會(huì)獲取到圖片的尺寸和類(lèi)型忽孽,該數(shù)據(jù)可供后續(xù)的縮放使用。

接下來(lái)需要根據(jù)原圖的大小和控件需要顯示的大小來(lái)設(shè)置內(nèi)存中應(yīng)加載的圖片的尺寸大小谢床,當(dāng)計(jì)算完畢以后再將圖片加載進(jìn)手機(jī)內(nèi)存中兄一,即可減少加載圖片時(shí)在內(nèi)存中的資源占用,下面是計(jì)算控件所需圖片大小的代碼:

public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?final int height = options.outHeight;

? ? ? ?final int width = options.outWidth;

? ? ? ?int inSampleSize = 1;

? ? ? ?if (height > reqHeight || width > reqWidth) {

? ? ? ? ? ? ? ? ? ? final int halfHeight = height / 2;

? ? ? ? ? ? ? ? ? ? final int halfWidth = width / 2;

? ? ? ? ? ? ? ? ? ?while ((halfHeight / inSampleSize) > reqHeight && ?(halfWidth / inSampleSize) > ? ? ? reqWidth) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? ?inSampleSize *= 2;

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ?}

? ? ?return inSampleSize;

}

該方法為設(shè)置BitmapFactory.Options 中inSampleSize 的值识腿,如圖片的原大小為2048x1536出革,當(dāng)inSampleSize 為4時(shí),將會(huì)得到一個(gè)約512x384大小的Bitmap渡讼。加載這張縮小的圖片僅僅使用大概0.75MB的內(nèi)存骂束,如果是加載完整尺寸的圖片,那么大概需要花費(fèi)12MB(前提都是Bitmap的配置是 ARGB_8888)成箫。

設(shè)置inSampleSize為2的冪是因?yàn)榻獯a器最終還是會(huì)對(duì)非2的冪的數(shù)進(jìn)行向下處理展箱,獲取到最靠近2的冪的數(shù)。詳情參考inSampleSize的文檔蹬昌。下面為加載任意大小圖片并設(shè)置為說(shuō)需的大小的代碼:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,

int reqWidth, int reqHeight) {

? ? ? ? ? ? ? ? ? final BitmapFactory.Options options = new BitmapFactory.Options();

? ? ? ? ? ? ? ? ? options.inJustDecodeBounds = true;

? ? ? ? ? ? ? ? ? BitmapFactory.decodeResource(res, resId, options);

? ? ? ? ? ? ? ? ? options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

? ? ? ? ? ? ? ? ? options.inJustDecodeBounds = false;

? ? ? ? ? ? ? ? ? return BitmapFactory.decodeResource(res, resId, options);

}

首先需要設(shè)置 inJustDecodeBounds 為 true, 把options的值傳遞過(guò)來(lái)混驰,但不加載圖片,不消耗內(nèi)存,然后設(shè)置 inSampleSize 的值栖榨。此時(shí)設(shè)置 inJustDecodeBounds 為 false昆汹,之后重新調(diào)用相關(guān)的解碼方法,指定大小的圖片便加載進(jìn)入了內(nèi)存當(dāng)中婴栽。如:

mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

到這里基本的加載相關(guān)已經(jīng)完成了满粗,但依然有很多問(wèn)題,比如圖片加載是不應(yīng)該在主線程完成的愚争。

二映皆、非UI線程處理Bitmap: 圖片的加載是耗時(shí)操作,尤其是網(wǎng)絡(luò)加載轰枝,需要在非UI線程中處理劫扒,此處使用AsyncTask演示在后臺(tái)線程中處理Bitmap以及處理并發(fā)(concurrency)的問(wèn)題。官網(wǎng)介紹簡(jiǎn)單情況比較青睞于用AsyncTask,復(fù)雜情況就不建議使用了狸膏。

class BitmapWorkerTask extends AsyncTask {

private final WeakReference imageViewReference;

private int data = 0;

public BitmapWorkerTask(ImageView imageView) {

imageViewReference = new WeakReference(imageView);}

@Override

protected Bitmap doInBackground(Integer... params) {

data = params[0];

return decodeSampledBitmapFromResource(getResources(), data, 100, 100));}

@Override

protected void onPostExecute(Bitmap bitmap) {

if (imageViewReference != null && bitmap != null) {

final ImageView imageView = imageViewReference.get();

if (imageView != null) {

imageView.setImageBitmap(bitmap);} ?} ?} ?}

以上為對(duì)異步加載的代碼封裝,讓加載操作在后臺(tái)運(yùn)行添怔,對(duì)以上代碼的調(diào)用方法如下:

public void loadBitmap(int resId, ImageView imageView) {

BitmapWorkerTask task = new BitmapWorkerTask(imageView);

task.execute(resId); ?}

異步加載的簡(jiǎn)單封裝與其他情況下使用AsyncTask并無(wú)太大差別湾戳,在這里不再進(jìn)行過(guò)多的介紹。下面將分析圖像的并發(fā)處理广料。

此處的問(wèn)題為在ListView砾脑、GridView、RecyclerView中我們?yōu)槊恳粋€(gè)item都添加了一個(gè)下載的task,在用戶滑動(dòng)控件的時(shí)候艾杏,會(huì)有一些item被回收韧衣,然而,task并沒(méi)有消失也并不知道哪個(gè)Item被回收了哪個(gè)是要先加載的购桑。接下來(lái)的這個(gè)類(lèi)就是用來(lái)解決此問(wèn)題的畅铭,AsyncDrawable類(lèi)也是理解此小節(jié)的一個(gè)關(guān)鍵類(lèi)。

static class AsyncDrawable extends BitmapDrawable { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

? ? ?private final WeakReference bitmapWorkerTaskReference; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

? ? ? public AsyncDrawable(Resources res, Bitmap bitmap,BitmapWorkerTask bitmapWorkerTask)

? ? ? {super(res, bitmap); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

? ? ? ?bitmapWorkerTaskReference =new WeakReference(bitmapWorkerTask);

? ? ? ?}

? ? ? ? public BitmapWorkerTask getBitmapWorkerTask() {

? ? ? ? ? ? ? ? ? ? ? return bitmapWorkerTaskReference.get();

? ? ? ? }

}

public void loadBitmap(int resId, ImageView imageView) {

? ? ? ? if (cancelPotentialWork(resId, imageView)) {

? ? ? ? ? ? ? ? ?final BitmapWorkerTask task = new BitmapWorkerTask(imageView);

? ? ? ? ? ? ? ? ?final AsyncDrawable asyncDrawable =new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);

? ? ? ? ? ? ? ? ?imageView.setImageDrawable(asyncDrawable);

? ? ? ? ? ? ? ? ?task.execute(resId);

? ? ? ? ? }

?}

結(jié)合loadBitmap來(lái)理解代碼意圖:在loadBitmap中創(chuàng)建了AsyncDrawable實(shí)例勃蜘, AsyncDrawable(getResources(), mPlaceHolderBitmap, task);第一個(gè)參數(shù)忽略硕噩,第二個(gè)參數(shù)為默認(rèn)bitmap圖片,第三個(gè)為需要異步加載的圖片如來(lái)自網(wǎng)絡(luò)缭贡,第一第二個(gè)參數(shù)是用來(lái)生成AsyncDrawable的默認(rèn)圖片的炉擅,它將會(huì)和目標(biāo)imageView(item中的圖片)進(jìn)行綁定并顯示一個(gè)默認(rèn)的圖片,同時(shí)執(zhí)行task.execute(resId)代碼阳惹,后臺(tái)將加載需要加載的圖片谍失,在圖片加載完成后將真正需要顯示的圖片顯示在imageView上。整個(gè)過(guò)程中AsyncDrawable起到了關(guān)鍵的綁定作用莹汤,在loadBitmap中使用到了cancelPotentialWork方法快鱼,該方法是檢查是否有另一個(gè)正在執(zhí)行的任務(wù)與該ImageView關(guān)聯(lián)了起來(lái),如果的確是這樣,它通過(guò)執(zhí)行cancel()方法來(lái)取消另一個(gè)任務(wù)攒巍,以此來(lái)保證執(zhí)行的是最新最近的task嗽仪,而不是某一個(gè)已經(jīng)過(guò)時(shí)的task。

public static boolean cancelPotentialWork(int data, ImageView imageView) {

? ? ?final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

? ? ?if (bitmapWorkerTask != null) {

? ? ? ? ? ? ? ? ?final int bitmapData = bitmapWorkerTask.data;

? ? ? ? ? ? ? ? ?if (bitmapData == 0 || bitmapData != data) {

? ? ? ? ? ? ? ? bitmapWorkerTask.cancel(true);

? ? ? ? ? ? ?} else {

? ? ? ? ? ? ? ? return false;

? ? ? ? ? ? }

? ? ?}

?return true;

?}

private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {

? ? ? ? ? ? ? ? if (imageView != null) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? ?final Drawable drawable = imageView.getDrawable();

? ? ? ? ? ? ? ? ? ? ? ? ? ? if (drawable instanceof AsyncDrawable) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?return asyncDrawable.getBitmapWorkerTask();

? ? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? return null;

}

getBitmapWorkerTask()被用作檢索AsyncTask是否已經(jīng)被分配到指定的ImageView

最后是更新BitmapWorkerTask的onPostExecute() 方法

protected void onPostExecute(Bitmap bitmap) {

? ? ? ? ? ? ?if (isCancelled()) {bitmap = null;}

? ? ? ? ? ? ?if (imageViewReference != null && bitmap != null) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? final ImageView imageView = imageViewReference.get();

? ? ? ? ? ? ? ? ? ? ? ? ? ? final BitmapWorkerTask bitmapWorkerTask =getBitmapWorkerTask(imageView);

? ? ? ? ? ? ? ? ? ? ? ? ? ? if (this == bitmapWorkerTask && imageView != null) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?imageView.setImageBitmap(bitmap);

? ? ? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

}

在onPostExecute中再次檢查imageview是否被回收柒莉,task是否被取消闻坚,task是否一致如果一致且imageview沒(méi)有被回收,然后再顯示圖像兢孝。以上就是并發(fā)加載的解決方案了窿凤,主要解決listview等空間在執(zhí)行循環(huán)加載和滾動(dòng)時(shí)的并發(fā)圖像加載問(wèn)題。

三跨蟹、緩存Bitmap:解決完了加載問(wèn)題雳殊,接下來(lái)要解決緩存問(wèn)題,因?yàn)椴豢赡芩械膱D片無(wú)論是否可重復(fù)利用均每次顯示都重新加載窗轩。使用內(nèi)存緩存與磁盤(pán)緩存可以提高響應(yīng)速度與UI流暢度夯秃。

內(nèi)存緩存以花費(fèi)寶貴的程序內(nèi)存為前提來(lái)快速訪問(wèn)位圖。LruCache類(lèi)(在API Level 4的Support Library中也可以找到)特別適合用來(lái)緩存Bitmaps痢艺,它使用一個(gè)強(qiáng)引用(strong referenced)的LinkedHashMap保存最近引用的對(duì)象仓洼,并且在緩存超出設(shè)置大小的時(shí)候剔除(evict)最近最少使用到的對(duì)象。當(dāng)加載Bitmap顯示到ImageView 之前堤舒,會(huì)先從LruCache 中檢查是否存在這個(gè)Bitmap色建。如果確實(shí)存在,它會(huì)立即被用來(lái)顯示到ImageView上舌缤,如果沒(méi)有找到箕戳,會(huì)觸發(fā)一個(gè)后臺(tái)線程去處理顯示該Bitmap任務(wù)。

public void loadBitmap(int resId, ImageView imageView) {

? ? ? ? ? ? ? final String imageKey = String.valueOf(resId);

? ? ? ? ? ? ?final Bitmap bitmap = getBitmapFromMemCache(imageKey);

? ? ? ? ? ? if (bitmap != null) {

? ? ? ? ? ? ? ? ? ? ?mImageView.setImageBitmap(bitmap);

? ? ? ? ? ?} else {

? ? ? ? ? ? ? ? ? ? ?mImageView.setImageResource(R.drawable.image_placeholder);

? ? ? ? ? ? ? ? ? ? ?BitmapWorkerTask task = new BitmapWorkerTask(mImageView);

? ? ? ? ? ? ? ? ? ? ?task.execute(resId);

? ? ? ? ? ? }

}

BitmapWorkerTask 需要在doInBackground中把解析好的Bitmap添加到內(nèi)存緩存中

內(nèi)存緩存能夠提高訪問(wèn)最近用過(guò)的Bitmap的速度国撵,但是我們無(wú)法保證最近訪問(wèn)過(guò)的Bitmap都能夠保存在緩存中陵吸。像類(lèi)似GridView等需要大量數(shù)據(jù)填充的控件很容易就會(huì)用盡整個(gè)內(nèi)存緩存。另外卸留,我們的應(yīng)用可能會(huì)被類(lèi)似打電話等行為而暫停并退到后臺(tái)走越,因?yàn)楹笈_(tái)應(yīng)用可能會(huì)被殺死,那么內(nèi)存緩存就會(huì)被銷(xiāo)毀耻瑟,里面的Bitmap也就不存在了旨指。一旦用戶恢復(fù)應(yīng)用的狀態(tài),那么應(yīng)用就需要重新處理那些圖片喳整。

磁盤(pán)緩存可以用來(lái)保存那些已經(jīng)處理過(guò)的Bitmap谆构,它還可以減少那些不再內(nèi)存緩存中的Bitmap的加載次數(shù)。當(dāng)然從磁盤(pán)讀取圖片會(huì)比從內(nèi)存要慢框都,而且由于磁盤(pán)讀取操作時(shí)間是不可預(yù)期的搬素,讀取操作需要在后臺(tái)線程中處理。內(nèi)存緩存的檢查是可以在UI線程中進(jìn)行的,磁盤(pán)緩存的檢查需要在后臺(tái)線程中處理熬尺。磁盤(pán)操作永遠(yuǎn)都不應(yīng)該在UI線程中發(fā)生摸屠。當(dāng)圖片處理完成后,Bitmap需要添加到內(nèi)存緩存與磁盤(pán)緩存中粱哼,方便之后的使用季二。該模塊可結(jié)合網(wǎng)上所介紹的Bitmap三級(jí)緩存的一些知識(shí)來(lái)具體的了解圖片的緩存機(jī)制。

以上只是在一些很基礎(chǔ)的android層面進(jìn)行了圖片加載的分析揭措,圖片加載框架很多都會(huì)在此基礎(chǔ)上進(jìn)行封裝胯舷,理解基礎(chǔ)的圖片加載對(duì)理解封裝了更多方法的框架將會(huì)有不小的幫助。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末绊含,一起剝皮案震驚了整個(gè)濱河市桑嘶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌躬充,老刑警劉巖逃顶,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異充甚,居然都是意外死亡口蝠,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)津坑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人傲霸,你說(shuō)我怎么就攤上這事疆瑰。” “怎么了昙啄?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵穆役,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我梳凛,道長(zhǎng)耿币,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任韧拒,我火速辦了婚禮淹接,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘叛溢。我一直安慰自己塑悼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布楷掉。 她就那樣靜靜地躺著厢蒜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上斑鸦,一...
    開(kāi)封第一講書(shū)人閱讀 51,624評(píng)論 1 305
  • 那天愕贡,我揣著相機(jī)與錄音,去河邊找鬼巷屿。 笑死固以,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的攒庵。 我是一名探鬼主播嘴纺,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼浓冒!你這毒婦竟也來(lái)了栽渴?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤稳懒,失蹤者是張志新(化名)和其女友劉穎闲擦,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體场梆,經(jīng)...
    沈念sama閱讀 45,722評(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,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖顶岸,靈堂內(nèi)的尸體忽然破棺而出腔彰,到底是詐尸還是另有隱情,我是刑警寧澤辖佣,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布霹抛,位于F島的核電站,受9級(jí)特大地震影響卷谈,放射性物質(zhì)發(fā)生泄漏杯拐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一世蔗、第九天 我趴在偏房一處隱蔽的房頂上張望端逼。 院中可真熱鬧,春花似錦污淋、人聲如沸裳食。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)诲祸。三九已至浊吏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間救氯,已是汗流浹背找田。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留着憨,地道東北人墩衙。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像甲抖,于是被迫代替她去往敵國(guó)和親漆改。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

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