開源一個 Android 圖片壓縮框架

在我們的業(yè)務場景中昏苏,需要使用客戶端采集圖片,上傳服務器威沫,然后對圖片信息進行識別贤惯。為了提升程序的性能,我們需要保證圖片上傳服務器的速度的同時棒掠,保證用于識別圖片的質量孵构。整個優(yōu)化包括兩個方面的內容:

  1. 相機拍照的優(yōu)化:包括相機參數(shù)的選擇、預覽句柠、啟動速度和照片質量等浦译;
  2. 圖片壓縮的優(yōu)化:基于拍攝的圖片和從相冊中選擇的圖片進行壓縮,控制圖片大小和尺寸溯职。

在本文中精盅,我們主要介紹圖片壓縮優(yōu)化,后續(xù)我們會介紹如何對 Android 的相機進行封裝和優(yōu)化谜酒。本項目主要基于 Android 自帶的圖片壓縮 API 進行封裝叹俏,結合了 LubanCompressor 的優(yōu)點,同時提供了用戶自定義壓縮策略的接口僻族。該項目的主要目的在于粘驰,統(tǒng)一圖片壓縮框庫的實現(xiàn),集成常用的兩種圖片壓縮算法述么,讓你以更低的成本集成圖片壓縮功能到自己的項目中蝌数。

1、圖片壓縮的基礎知識

對于一般業(yè)務場景度秘,當我們展示圖片的時候顶伞,Glide 會幫我們處理加載的圖片的尺寸問題。但在把采集來的圖片上傳到服務器之前,為了節(jié)省流量唆貌,我們需要對圖片進行壓縮滑潘。

在 Android 平臺上,默認提供的壓縮有三種方式:質量壓縮和兩種尺寸壓縮锨咙,鄰近采樣以及雙線性采樣语卤。下面我們簡單介紹下者三種壓縮方式都是如何使用的:

1.1 質量壓縮

所謂的質量壓縮就是下面的這行代碼,它是 Bitmap 的方法酪刀。當我們得到了 Bitmap 的時候粹舵,即可使用這個方法來實現(xiàn)質量壓縮。它一般位于我們所有壓縮方法的最后一步蓖宦。

// android.graphics齐婴。Bitmap
compress(CompressFormat format, int quality, OutputStream stream)

該方法接受三個參數(shù)油猫,其含義分別如下:

  1. format:枚舉稠茂,有三個選項 JPEG, PNGWEBP,表示圖片的格式情妖;
  2. quality:圖片的質量睬关,取值在 [0,100] 之間,表示圖片質量毡证,越大电爹,圖片的質量越高;
  3. stream:一個輸出流料睛,通常是我們壓縮結果輸出的文件的流

1.2 鄰近采樣

鄰近采樣基于臨近點插值算法丐箩,用像素代替周圍的像素。鄰近采樣的核心代碼只有下面三行恤煞,

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 1;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.blue_red, options);

鄰近采樣核心的地方在于 inSampleSize 的計算屎勘。它通常是我們使用的壓縮算法的第一步。我們可以通過設置 inSampleSize 來得到原始圖片采樣之后的結果居扒,而不是將原始的圖片全部加載到內存中概漱,以防止 OOM。標準使用姿勢如下:

    // 獲取原始圖片的尺寸
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    options.inSampleSize = 1;
    BitmapFactory.decodeStream(srcImg.open(), null, options);
    this.srcWidth = options.outWidth;
    this.srcHeight = options.outHeight;

    // 進行圖片加載喜喂,此時會將圖片加載到內存中
    options.inJustDecodeBounds = false;
    options.inSampleSize = calInSampleSize();
    Bitmap bitmap = BitmapFactory.decodeStream(srcImg.open(), null, options);

這里主要分成兩個步驟瓤摧,它們各自的含義是:

  1. 先通過設置 Options 的 inJustDecodeBounds 為 true,來加載圖片玉吁,以得到圖片的尺寸信息照弥。此時圖片不會被加載到內存中,所以不會造成 OOM进副,同時我們可以通過 Options 得到原圖的尺寸信息这揣。
  2. 根據(jù)上一步中得到的圖片的尺寸信息,計算一個 inSampleSize,然后將 inJustDecodeBounds 設置為 false曾沈,以加載采樣之后的圖片到內存中这嚣。

關于 inSampleSize 需要簡單說明一下:inSampleSize 代表壓縮后的圖像一個像素點代表了原來的幾個像素點,例如 inSampleSize 為 4塞俱,則壓縮后的圖像的寬高是原來的 1/4姐帚,像素點數(shù)是原來的 1/16,inSampleSize 一般會選擇 2 的指數(shù)障涯,如果不是 2 的指數(shù)罐旗,內部計算的時候也會向 2 的指數(shù)靠近。所以唯蝶,實際使用過程中九秀,我們會通過明確指定 inSampleSize 為 2 的指數(shù),來避免內部計算導致的不確定性粘我。

1.3 雙線性采樣

鄰近采樣可以對圖片的尺寸進行有效的控制鼓蜒,但是它存在幾個問題。比如征字,當我需要把圖片的寬度壓縮到 1200 左右的時候都弹,如果原始的圖片的寬度壓是 3200,那么我只能通過設置 inSampleSize 將采樣率設置為 2 來將其壓縮到 1600. 此時圖片的尺寸比我們的要求要大匙姜。就是說畅厢,鄰近采樣無法對圖片的尺寸進行更加精準的控制。如果需要對圖片尺寸進行更加精準的控制氮昧,那么就需要使用雙線性壓縮了框杜。

雙線性采樣采用雙線性插值算法,相比鄰近采樣簡單粗暴的選擇一個像素點代替其他像素點袖肥,雙線性采樣參考源像素相應位置周圍 2x2 個點的值咪辱,根據(jù)相對位置取對應的權重,經(jīng)過計算得到目標圖像昭伸。

它在 Android 中的使用也比較簡單梧乘,

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.blue_red);
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 0.5f);
Bitmap sclaedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth()/2, bitmap.getHeight()/2, matrix, true);

也就是對得到的 Bitmap 應用 createBitmap() 進行處理,并傳入 Matrix 指定圖片尺寸放縮的比例庐杨。該方法返回的 Bitmap 就是雙線性壓縮之后的結果选调。

1.4 圖片壓縮算法總結

在實際使用過程中,我們通常會結合三種壓縮方式使用灵份,一般使用的步驟如下仁堪,

  1. 使用鄰近采樣對原始的圖片進行采樣,將圖片控制到比目標尺寸稍大的大小填渠,防止 OOM弦聂;
  2. 使用雙線性采樣對圖片的尺寸進行壓縮鸟辅,控制圖片的尺寸為目標的大小莺葫;
  3. 對上述兩個步驟之后得到的圖片 Bitmap 進行質量壓縮匪凉,并將其輸出到磁盤上。

當然捺檬,本質上 Android 圖片的編碼是由 Skia 庫來完成的再层,所以,除了使用 Android 自帶的庫進行壓縮堡纬,我們還可以調用外部的庫進行壓縮聂受。為了追求更高的壓縮效率,通常我們會在 Native 層對圖片進行處理烤镐,這將涉及 JNI 的知識蛋济。筆者曾在之前的文章 《在 Android 中使用 JNI 的總結》 中介紹過 Android 平臺上 JNI 的調用的常規(guī)思路,感興趣的同學可以參考下炮叶。

2碗旅、Github 上的開源的圖片壓縮庫

現(xiàn)在 Github 上的圖片壓縮框架主要有 Luban 和 Compressor 兩個。Star 的數(shù)量也比較高悴灵,一個 9K扛芽,另一個 4K. 但是,這兩個圖片壓縮的庫有各自的優(yōu)點和缺點积瞒。下面我們通過一個表格總結一下:

框架 優(yōu)點 缺點
Luban 據(jù)說是根據(jù)微信圖片壓縮逆推的算法 1.只適用于一般的圖片展示的場景,無法對圖片的尺寸進行精準壓縮登下;2.內部封裝 AsyncTaks 來進行異步的圖片壓縮茫孔,對于 RxJava 支持不好。
Compressor 1.可以對圖片的尺寸進行壓縮被芳;2.支持 RxJava缰贝。 1.尺寸壓縮的場景有限,如果有特別的需求畔濒,則需要手動修改源代碼剩晴;2.圖片壓縮采樣的時候計算有問題,導致采樣后的圖片尺寸總是小于我們指定的尺寸

上面的圖表已經(jīng)總結得很詳細了侵状。所以赞弥,根據(jù)上面的兩個庫各自的優(yōu)缺點,我們打算開發(fā)一個新的圖片壓縮框架趣兄。它滿足下面的功能:

  1. 支持 RxJava:我們可以像使用 Compressor 的時候那樣绽左,指定圖片壓縮的線程和結果監(jiān)聽的線程;
  2. 支持 Luban 壓縮算法:Luban 壓縮算法核心的部分只在于 inSampleSize 的計算艇潭,因此拼窥,我們可以很容易得將其集成到我們的新的庫中戏蔑。之所以加入 Luban,是為了讓我們的庫可以適用于一般圖片展示的場景鲁纠。用戶無需指定圖片的尺寸总棵,用起來省心省力。
  3. 支持 Compressor 壓縮算法同時指定更多的參數(shù):Compressor 壓縮算法就是我們上述提到的三種壓縮算法的總和改含。不過彻舰,當要壓縮的寬高比與原始圖片的寬高比不一致的時候,它只提供了一種情景候味。下文中介紹我們框架的時候會說明進行更詳細的說明刃唤。當然,你可以在調用框架的方法之前主動去計算出一個寬高比白群,但是你需要把圖片壓縮的第一個階段主動走一遍尚胞,費心費力。
  4. 提供用戶自定義壓縮算法的接口:我們希望設計的庫可以允許用戶自定義壓縮策略帜慢。在想要替換圖片壓縮算法的時候笼裳,通過鏈式調用的一個方法直接更換策略即可。即粱玲,我們希望能夠讓用戶以最低的成本替換項目中的圖片壓縮算法躬柬。

3、項目整體架構

以下是我們的圖片壓縮框架的整體架構抽减,這里我們只列舉除了其中核心的部分代碼允青。這里的 Compress 是我們的鏈式調用的起點,我們可以用它來指定圖片壓縮的基本參數(shù)卵沉。然后颠锉,當我們使用它的 strategy() 方法之后,方法將進入到圖片壓縮策略中史汗,此時琼掠,我們繼續(xù)鏈式調用壓縮策略的自定義方法,個性化地設置各壓縮策略自己的參數(shù):

項目整體架構

這里的所有的壓縮策略都繼承自抽線的基類 AbstractStrategy停撞,它提供了兩個默認的實現(xiàn) Luban 和 Compressor. 接口 CompressListener 和 CacheNameFactory 分別用來監(jiān)聽圖片壓縮進度和自定義壓縮的圖片的名稱瓷蛙。下面的三個是圖片相關的工具類,用戶可以調用它們來實現(xiàn)自己壓縮策略戈毒。

4艰猬、使用

首先,在項目的 Gradle 中加入我的 Maven 倉庫的地址:

maven { url "https://dl.bintray.com/easymark/Android" }

然后副硅,在你的項目的依賴中姥宝,添加該庫的依賴:

implementation 'me.shouheng.compressor:compressor:0.0.1'

然后,就可以在項目中使用了恐疲。你可以參考 Sample 項目的使用方式腊满。不過套么,下面我們還是對它的一些 API 做簡單的說明。

4.1 Luban 的使用

下面是 Luban 壓縮策略的使用示例碳蛋,它與 Luban 庫的使用類似胚泌。只是在 Luban 的庫的基礎上,我們增加了一個 copy 的選項肃弟,用來表示當圖片因為小于指定的大小而沒有被壓縮之后玷室,是否將原始的圖片拷貝到指定的目錄。因為笤受,比如當你使用回調獲取圖片壓縮結果的時候穷缤,如果按照 Luban 庫的邏輯,你得到的是原始的圖片箩兽,所以津肛,此時你需要額外進行判斷。因此汗贫,我們增加了這個布爾類型的參數(shù)身坐,你可以通過它指定將原始文件進行拷貝,這樣你就不需要在回調中對是否是原始圖片進行判斷了落包。

    // 在 Compress 的 with() 方法中指定 Context 和 要壓縮文件 File
    val luban = Compress.with(this, file)
        // 這里添加一個回調部蛇,如果你不使用 RxJava,那么可以用它來處理壓縮的結果
        .setCompressListener(object : CompressListener{
            override fun onStart() {
                LogUtils.d(Thread.currentThread().toString())
                Toast.makeText(this@MainActivity, "Compress Start", Toast.LENGTH_SHORT).show()
            }

            override fun onSuccess(result: File?) {
                LogUtils.d(Thread.currentThread().toString())
                displayResult(result?.absolutePath)
                Toast.makeText(this@MainActivity, "Compress Success : $result", Toast.LENGTH_SHORT).show()
            }

            override fun onError(throwable: Throwable?) {
                LogUtils.d(Thread.currentThread().toString())
                Toast.makeText(this@MainActivity, "Compress Error :$throwable", Toast.LENGTH_SHORT).show()
            }
        })
        // 壓縮圖片的名稱工廠方法咐蝇,用來指定壓縮結果的文件名
        .setCacheNameFactory { System.currentTimeMillis().toString() }
        // 圖片的質量
        .setQuality(80)
        // 上面基本的配置完了涯鲁,下面指定圖片的壓縮策略為 Luban
        .strategy(Strategies.luban())
        // 指定如果圖片小于等于 100K 就不壓縮了,這里的參數(shù) copy 表示嘹害,如果不壓縮的話要不要拷貝文件
        .setIgnoreSize(100, copy)

        // 按上面那樣得到了 Luban 實例之后有下面兩種方式啟動圖片壓縮
        // 啟動方式 1:使用 RxJava 進行處理
        val d = luban.asFlowable()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { displayResult(it.absolutePath) }
    
        // 啟動方式 2:直接啟動撮竿,此時使用內部封裝的 AsyncTask 進行壓縮,壓縮結果只能在上面的回調中進行處理了
        luban.launch()

4.2 Compressor 的使用

下面是 Compressor 壓縮策略的基本的使用笔呀,在調用 strategy() 方法指定壓縮策略之前,你的任務與 Luban 一致髓需。所以许师,如果你需要更換圖片壓縮算法的時候,直接使用 strategy() 方法更換策略即可僚匆,前面部分的邏輯無需改動微渠,因此,可以降低你更換壓縮策略的成本咧擂。

    val compressor = Compress.with(this, file)
        .setQuality(60)
        .setTargetDir("")
        .setCompressListener(object : CompressListener {
            override fun onStart() {
                LogUtils.d(Thread.currentThread().toString())
                Toast.makeText(this@MainActivity, "Compress Start", Toast.LENGTH_SHORT).show()
            }

            override fun onSuccess(result: File?) {
                LogUtils.d(Thread.currentThread().toString())
                displayResult(result?.absolutePath)
                Toast.makeText(this@MainActivity, "Compress Success : $result", Toast.LENGTH_SHORT).show()
            }

            override fun onError(throwable: Throwable?) {
                LogUtils.d(Thread.currentThread().toString())
                Toast.makeText(this@MainActivity, "Compress Error :$throwable", Toast.LENGTH_SHORT).show()
            }
        })
        .strategy(Strategies.compressor())
        .setMaxHeight(100f)
        .setMaxWidth(100f)
        .setScaleMode(Configuration.SCALE_SMALLER)
        .launch()

這里的 setMaxHeight(100f)setMaxWidth(100f) 用來表示圖片壓縮的目標大小逞盆。具體的大小是如何計算的呢?在 Compressor 庫中你是無法確定的松申,但是在我們的庫中云芦,你可以通過 setScaleMode() 方法來指定俯逾。這個方法接收一個整數(shù)類型的枚舉,它的取值范圍有 4 個舅逸,即 SCALE_LARGER, SCALE_SMALLER, SCALE_WIDTHSCALE_HEIGHT桌肴,它們具體的含義我們會進行詳細說明。這里我們默認的壓縮方式是 SCALE_LARGER琉历,也就是 Compressor 庫的壓縮方式坠七。那么這四個參數(shù)分別是什么含義呢?

這里我們以一個例子來說明旗笔,假設有一個圖片的寬度是 1000彪置,高度是 500,簡寫作 (W:1000, H:500)蝇恶,通過 setMaxHeight()setMaxWidth() 指定的參數(shù)均為 100拳魁,那么,就稱目標圖片的尺寸艘包,寬度是 100的猛,高度是 100,簡寫作 (W:100, H:100)想虎。那么按照上面的四種壓縮方式卦尊,最終的結果將是:

  • SCALE_LARGER:對高度和長度中較大的一個進行壓縮,另一個自適應舌厨,因此壓縮結果是 (W:100, H:50). 也就是說岂却,因為原始圖片寬高比 2:1,我們需要保持這個寬高比之后再壓縮裙椭。而目標寬高比是 1:1. 而原圖的寬度比較大躏哩,所以,我們選擇將寬度作為壓縮的基準揉燃,寬度縮小 10 倍扫尺,高度也縮小 10 倍。這是 Compressor 庫的默認壓縮策略炊汤,顯然它只是優(yōu)先使得到的圖片更小正驻。這在一般情景中沒有問題,但是當你想把短邊控制在 100 就無計可施了(需要計算之后再傳參)抢腐,此時可以使用 SCALE_SMALLER姑曙。
  • SCALE_SMALLER:對高度和長度中較大的一個進行壓縮,另一個自適應迈倍,因此壓縮結果是 (W:200, H:100). 也就是伤靠,高度縮小 5 倍之后,達到目標 100啼染,然后寬度縮小 5 倍宴合,達到 200.
  • SCALE_WIDTH:對寬度進行壓縮焕梅,高度自適應。因此得到的結果與 SCALE_LARGER 一致形纺。
  • SCALE_HEIGHT:對高度進行壓縮丘侠,寬度自適應,因此得到的結果與 SCALE_HEIGHT 一致逐样。

4.3 自定義策略

自定義一個圖片壓縮策略也是很簡單的蜗字,你可以通過繼承 SimpleStrategy 或者直接繼承 AbstractStrategy 來實現(xiàn):

class MySimpleStrategy: SimpleStrategy() {

    override fun calInSampleSize(): Int {
        return 2
    }

    fun myLogic(): MySimpleStrategy {
        return this
    }

}

注意下,如果想要實現(xiàn)鏈式的調用脂新,自定義壓縮策略的方法需要返回自身挪捕。

5、最后

因為我們的項目中争便,需要把圖片的短邊控制到 1200级零,長變只適應,只通過改變 Luban 來改變采樣率只能把邊長控制到一個范圍中滞乙,無法精準壓縮奏纪。所以,我們想到了 Compressor斩启,并提出了 SCALE_SMALLER 的壓縮模式. 但是 Luban 也不是用不到序调,一般用來展示的圖片的壓縮,它用起來更加方便兔簇。因此发绢,我們在庫中綜合了兩個框架,其實代碼量并不大垄琐。當然边酒,為了讓我們的庫功能更加豐富,因此我們提出了自定義壓縮策略的接口狸窘,也是用來降低壓縮策略的更換成本吧墩朦。

最后項目開源在 Github,地址是:https://github.com/Shouheng88/Compressor. 歡迎 Star 和 Fork翻擒,為該項目貢獻代碼或者提出 issue :)

后續(xù)介杆,筆者會對 Android 端的相機優(yōu)化和 JNI 操作 OpenCV 進行圖片處理進行講解,感興趣的關注作者呦 :)

獲取更多技術文章可以直接關注我的公眾號「Hello 開發(fā)者」韭寸,另外感興趣的可以加入技術 QQ 交流群:1018235573.

以上,感謝閱讀~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末荆隘,一起剝皮案震驚了整個濱河市恩伺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌椰拒,老刑警劉巖晶渠,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凰荚,死亡現(xiàn)場離奇詭異,居然都是意外死亡褒脯,警方通過查閱死者的電腦和手機便瑟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來番川,“玉大人到涂,你說我怎么就攤上這事“涠剑” “怎么了践啄?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長沉御。 經(jīng)常有香客問我屿讽,道長,這世上最難降的妖魔是什么吠裆? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任伐谈,我火速辦了婚禮,結果婚禮上试疙,老公的妹妹穿的比我還像新娘诵棵。我一直安慰自己,他們只是感情好效斑,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布非春。 她就那樣靜靜地躺著,像睡著了一般缓屠。 火紅的嫁衣襯著肌膚如雪奇昙。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天敌完,我揣著相機與錄音储耐,去河邊找鬼。 笑死滨溉,一個胖子當著我的面吹牛什湘,可吹牛的內容都是我干的。 我是一名探鬼主播晦攒,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼闽撤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了脯颜?” 一聲冷哼從身側響起哟旗,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后闸餐,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饱亮,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年舍沙,在試婚紗的時候發(fā)現(xiàn)自己被綠了近上。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡拂铡,死狀恐怖壹无,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情和媳,我是刑警寧澤格遭,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站留瞳,受9級特大地震影響拒迅,放射性物質發(fā)生泄漏。R本人自食惡果不足惜她倘,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一璧微、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧硬梁,春花似錦前硫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至跃巡,卻和暖如春危号,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背素邪。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工外莲, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人兔朦。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓偷线,卻偏偏與公主長得像,于是被迫代替她去往敵國和親沽甥。 傳聞我的和親對象是個殘疾皇子声邦,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內容

  • "錦繡你這狠苦的女人,我怨你不得好死摆舟。我怨你死不暝目"茶靡嘟囔了好幾遍翔忽。聲音越來越大英融。 "姐姐,你干什么歇式!你為什么...
    南玙茶靡閱讀 226評論 0 2
  • 站在大四這個臨界點材失,總會充斥著迷茫與不安,最近這些天硫豆,思考了很多龙巨,有關人性,有關生活熊响,有關親情旨别,友情,愛情汗茄,更...
    Round吳閱讀 159評論 0 0
  • 今兒整理物件秸弛,發(fā)現(xiàn)紅色的戒指盒,結婚戒指成雙成對的躺在哪里洪碳,原來已有好多年了递览,我?guī)缀鯇⑺z忘,由于時間變化瞳腌,粗糙雙...
    毛毛蟲的蝴蝶夢閱讀 316評論 0 5
  • 1. 同學聚會嫂侍,多年不見儿捧,敘舊之后,大家免不了你一言我一語的說起自己的孩子挑宠,婚姻菲盾,不亦樂乎。 我注意到只有寧寧沉默...
    北方的橙子閱讀 769評論 0 7
  • 花了一兩小時看了看薛之謙的八卦痹栖,覺得自己已經(jīng)被娛樂時代淘汰了亿汞。初中的時候對明星八卦如數(shù)家珍,甚至臺灣十八線小藝人的...
    魚粥田辛閱讀 667評論 5 3