RxJava學(xué)習(xí):一個(gè)簡(jiǎn)單的Demo讓你了解Rxjava的原理

客戶需求

/**
 * 根據(jù)一個(gè)Cat-Sdk提供的API盯质,通過關(guān)鍵字來搜索網(wǎng)絡(luò)中符合要求的所有貓的圖片,根據(jù)
 * 其可愛程度的數(shù)據(jù)贾惦,將這張最可愛的圖片保存到本地中凳谦。
 * 完成任務(wù):
 * 1酌毡、下載返回的貓的圖片
 * 2、找到最可愛的那張圖片
 * 3、保存到本地
 */

Cat-Sdk API (Version 1.0)

  • model類

      public class Cat implements Comparable<Cat> {
    
          private Bitmap mBitmap;
          private int mCuteness;
      
          @TargetApi(Build.VERSION_CODES.KITKAT)
          @Override
          public int compareTo(Cat another) {
              return Integer.compare(mCuteness, another.mCuteness);
          }
      }
    
  • 阻塞式interface

      public interface Api {
      
          // 通過查詢條件查詢所有符合條件的貓
          List<Cat> queryCats1(String query);
          // 保存最可愛的貓,并返回保存路徑
          Uri store1(Cat cat);
      }
    

業(yè)務(wù)邏輯

最理想的做法:阻塞調(diào)用
    public class CatsHelper {
        private Api api;
        public Uri saveTheCutestCat1(String query) {
            List<Cat> catList = api.queryCats1(query);
            Cat cutestCat = findCutestCat(catList);
            Uri savedUri = api.store1(cutestCat);
            return savedUri;
        } 
        private Cat findCutestCat(List<Cat> catList) {
            return Collections.max(catList);
        }
     }

上面的代碼是不是很清晰忌傻?是的沉唠。saveTheCutestCat1方法使用參數(shù)來調(diào)用這些方法疆虚。當(dāng)一個(gè)方法執(zhí)行完并返回結(jié)果后,再利用這個(gè)返回結(jié)果去調(diào)用下一個(gè)方法满葛。除了簡(jiǎn)單有效以外径簿,還有其他優(yōu)點(diǎn):

  • 組合功能(Composition)

    saveTheCutestCat1 由其他三個(gè)方法調(diào)用所組成,通過方法來把一個(gè)大功能分割為每個(gè)容易理解的小功能嘀韧。通過方法調(diào)用來組合使用這些小功能篇亭,使用和理解起來都相當(dāng)簡(jiǎn)單。

  • 異常處理

    每個(gè)方法都可以通過拋出異常來結(jié)束運(yùn)行锄贷,該異骋氲伲可以在拋出異常的方法里面處理曼月,也可以在調(diào)用該方法的外面處理。無需每次都處理每個(gè)異常柔昼,我們還可以在一個(gè)地方處理所有可能拋出的異常哑芹,直接使用try...catch語句或者向外拋出。

理想是好的岳锁,但現(xiàn)實(shí)是殘酷的绩衷。在Android中不可能只使用阻塞調(diào)用,而我們更多的是使用異步回調(diào)激率。

異步回調(diào)

這里我們先不去關(guān)心Cat-Sdk API使用異步調(diào)用的api去訪問網(wǎng)絡(luò)資源咳燕,暫時(shí)假設(shè)我們已經(jīng)獲取到了網(wǎng)絡(luò)資源的結(jié)果了。那么新的API接口變?yōu)檫@樣了:

public interface Api {
        
     interface CatsQueryCallback {
        void onCatListReceived(List<Cat> catList);

        void onError(Exception e);
    }
    interface StoreCallback {
        void onCatStored(Uri uri);

        void onStoredFail(Exception e);
    }
    List<Cat> queryCats2(String query, CatsQueryCallback callback);
    Uri store2(Cat cat, StoreCallback callback);
}

那我們的的CatsHelper也要發(fā)生改變了:

public class CatsHelper {
    private Api api;

    public interface CutestCatCallback {
        void onCutestCatSaved(Uri uri);

        void onQueryFailed(Exception e);
    }

    public void saveTheCutestCat2(String query, final CutestCatCallback cutestCatCallback) {

        api.queryCats2(query, new Api.CatsQueryCallback() {
            @Override
            public void onCatListReceived(List<Cat> catList) {
                Cat cutestCat = findCutestCat(catList);
                api.store2(cutestCat, new Api.StoreCallback() {
                    @Override
                    public void onCatStored(Uri uri) {
                        cutestCatCallback.onCutestCatSaved(uri);
                    }

                    @Override
                    public void onStoredFail(Exception e) {
                        cutestCatCallback.onQueryFailed(e);
                    }
                });
            }

            @Override
            public void onError(Exception e) {
                cutestCatCallback.onQueryFailed(e);
            }
        });
    }

這個(gè)這個(gè)...乒躺。與阻塞調(diào)用比起來招盲,一個(gè)是天堂, 一個(gè)是地獄啊嘉冒。業(yè)務(wù)邏輯雖然是一樣的曹货,但是有太多的干擾代碼了;太多的的匿名內(nèi)部類了讳推;組合功能也不見了顶籽,需通過回調(diào)接口手動(dòng)處理;異常也不會(huì)自動(dòng)傳遞了银觅,也需要手動(dòng)的處理礼饱。上面的代碼不僅長得惡心,并且更難發(fā)現(xiàn)潛在的bug究驴。那怎么辦了镊绪?怎么辦了?

泛型接口

通過觀察這三個(gè)接口(CatsQueryCallback, StoreCallback, CutestCatCallback)洒忧,會(huì)發(fā)現(xiàn)一個(gè)共同點(diǎn):

(1) 都有一個(gè)方法來返回結(jié)果(onCutestCatSaved, onCatListReceived, onCatStored)

(2) 都有一個(gè)方法來返回異常(onQueryFailed, onError, onStoredFail)

所以蝴韭,我們可以使用一個(gè)泛型接口來替代這三個(gè)接口

public interface Callback<T> {
    void onResult(T result);

    void onError(Exception e);
}

由于我們無法修改SDK中api中方法的參數(shù),所以需新創(chuàng)建一個(gè)包裝類

public class ApiWrapper {
    private Api api;

    public void queryCats(String query, final Callback<List<Cat>> catsCallback) {
        api.queryCats2(query, new Api.CatsQueryCallback() {
            @Override
            public void onCatListReceived(List<Cat> catList) {
                catsCallback.onResult(catList);
            }

            @Override
            public void onError(Exception e) {
                catsCallback.onError(e);
            }
        });
    }

    public void store(Cat cat, final Callback<Uri> uriCallback) {
        api.store2(cat, new Api.StoreCallback() {
            @Override
            public void onCatStored(Uri uri) {
                uriCallback.onResult(uri);
            }

            @Override
            public void onStoredFail(Exception e) {
                uriCallback.onError(e);
            }
        });
    }       
}

再來看看我們的CatsHelper類

public class CatsHelper {
    private ApiWrapper wrapper;
    public void saveTheCutestCat3(String query, final Callback<Uri> cutestCallback) {
        wrapper.queryCats(query, new Callback<List<Cat>>() {
            @Override
            public void onResult(List<Cat> result) {
                Cat cutestCat = findCutestCat(result);
                wrapper.store(cutestCat, cutestCallback);
            }

            @Override
            public void onError(Exception e) {
                cutestCallback.onError(e);
            }
     });
}

嗯熙侍,不錯(cuò)榄鉴。與上一版的CatsHelper比較來,看起來確實(shí)舒服多了蛉抓。那么還可以再優(yōu)化嗎牢硅?

分離參數(shù)和回調(diào)接口

我們繼續(xù)來觀察它們的共同點(diǎn):queryCats,store芝雪,saveTheCutestCat3這三個(gè)異步方法的參數(shù)都是一個(gè)回調(diào)接口和一般參數(shù),那么把這個(gè)一般參數(shù)和回調(diào)接口分離综苔,讓異步操作只管理一般參數(shù)惩系,而返回一個(gè)臨時(shí)對(duì)象來管理回調(diào)接口位岔。

首先創(chuàng)建一個(gè)管理回調(diào)接口的臨時(shí)對(duì)象

public abstract class AsyncJob<T> {

    public abstract void start(Callback<T> callback);
}

再修改我們的api包裝類

public class ApiWrapper {
    private Api api;
    public AsyncJob<List<Cat>> queryCats2(final String query) {

        return new AsyncJob<List<Cat>>() {
            @Override
            public void start(final Callback<List<Cat>> callback) {
                api.queryCats2(query, new Api.CatsQueryCallback() {
                    @Override
                    public void onCatListReceived(List<Cat> catList) {
                        callback.onResult(catList);
                    }
                    @Override
                    public void onError(Exception e) {
                        callback.onError(e);
                    }
                });
            }
        };
    }
    public AsyncJob<Uri> store2(final Cat cat) {

        return new AsyncJob<Uri>() {
            @Override
            public void start(final Callback<Uri> callback) {
                api.store2(cat, new Api.StoreCallback() {
                    @Override
                    public void onCatStored(Uri uri) {
                        callback.onResult(uri);
                    }
                    @Override
                    public void onStoredFail(Exception e) {
                        callback.onError(e);
                    }
                });
            }
        };
    }
}

最后修改我們的CatsHelper類

public class CatsHelper {
    private ApiWrapper wrapper;
    public AsyncJob<Uri> saveTheCutestCat4(final String query) {
        final AsyncJob<List<Cat>> catListAsyncJob = wrapper.queryCats2(query);
        
        final AsyncJob<Cat> cutestCatAsyncJob = new AsyncJob<Cat>() {
            @Override
            public void start(final Callback<Cat> callback) {
                catListAsyncJob.start(new Callback<List<Cat>>() {
                    @Override
                    public void onResult(List<Cat> result) {
                        callback.onResult(findCutestCat(result));
                    }

                    @Override
                    public void onError(Exception e) {
                        callback.onError(e);
                    }
                });
            }
        };
        AsyncJob<Uri> storedUriAsyncJob = new AsyncJob<Uri>() {
            @Override
            public void start(final Callback<Uri> callback) {
                cutestCatAsyncJob.start(new Callback<Cat>() {
                    @Override
                    public void onResult(Cat result) {
                        wrapper.store2(result)
                                .start(new Callback<Uri>() {
                                    @Override
                                    public void onResult(Uri result) {
                                        callback.onResult(result);
                                    }

                                    @Override
                                    public void onError(Exception e) {
                                        callback.onError(e);
                                    }
                                });
                    }

                    @Override
                    public void onError(Exception e) {
                        callback.onError(e);
                    }
                });
            }
        };
        return storedUriAsyncJob;
    }
}

上面的代碼將整個(gè)流程分解為幾個(gè)操作,數(shù)據(jù)流向?yàn)椋?/p>

         (async)                 (sync)           (async)
query ===========> List<Cat> -------------> Cat ==========> Uri
    queryCats              findCutest          store

雖然findCutest標(biāo)記的是sync的堡牡,是相對(duì)qurey和store來說的抒抬,但在整個(gè)操作中,他還是屬于異步的晤柄。因?yàn)槿绻粋€(gè)操作是異步的擦剑,則每個(gè)調(diào)用該異步操作的方法也是異步的。

上面的代碼量雖然增多了芥颈,但看起來還是比較清晰的惠勒,并且更加容易理解上面的異步操作:catListAsyncJob, cutestCatAsyncJob, storedUriAsyncJob. 不過,這個(gè)saveTheCutestCat4比saveTheCutestCat3有什么優(yōu)勢(shì)嗎爬坑?這個(gè)這個(gè)...纠屋。好像是沒有哦。好吧盾计。我們繼續(xù)優(yōu)化售担。

簡(jiǎn)單的映射

在cutestCatAsyncJob那一塊代碼中,其實(shí)核心的邏輯只有findCutestCat(result)署辉,其他的代碼只是為了啟動(dòng)AnsyJob并接收結(jié)果和處理異常的干擾代碼族铆。但是這些代碼是通用的,我們可以把他們放到其他地方來讓我們更加專注業(yè)務(wù)邏輯代碼哭尝。How to do?

通過一個(gè)轉(zhuǎn)換方法來轉(zhuǎn)換AsyncJob 的結(jié)果哥攘。但由于Java的限制,無法把方法作為參數(shù)刚夺,所以需要用一個(gè)接口(或者類)并在里面定義一個(gè)轉(zhuǎn)換函數(shù):

public interface Func<T, R> {

    R call(T t);
}

當(dāng)我們把 AsyncJob 的結(jié)果轉(zhuǎn)換為其他類型的時(shí)候献丑, 我們需要把一個(gè)結(jié)果值映射為另外一種類型,這個(gè)操作我們稱之為 map侠姑。 把該函數(shù)定義到 AsyncJob 類中比較方便创橄,這樣就可以通過 this 來訪問 AsyncJob 對(duì)象了。

public abstract class AsyncJob<T> {

    public abstract void start(Callback<T> callback);

    public <R> AsyncJob<R> map(final Func<T, R> func) {
        final AsyncJob<T> source = this;
        return new AsyncJob<R>() {
            @Override
            public void start(final Callback<R> callback) {
                source.start(new Callback<T>() {
                    @Override
                    public void onResult(T result) {
                        R mapped = func.call(result);
                        callback.onResult(mapped);
                    }

                    @Override
                    public void onError(Exception e) {
                        callback.onError(e);
                    }
                });
            }
        };
    }
}

再來看看CatsHelper類

public class CatsHelper {
    private ApiWrapper wrapper;
    public AsyncJob<Uri> saveTheCutestCat5(final String query) {
        final AsyncJob<List<Cat>> catListAsyncJob = wrapper.queryCats2(query);
        final AsyncJob<Cat> cutestCatAsyncJob = catListAsyncJob.map(new Func<List<Cat>, Cat>() {

            @Override
            public Cat call(List<Cat> catList) {
                return findCutestCat(catList);
            }
        });
         //方式一:
        AsyncJob<Uri> storedUriAsyncJob = cutestCatAsyncJob.map(new Func<Cat, Uri>() {
            @Override
            public Uri call(Cat cat) {
                AsyncJob<Uri> store2 = wrapper.store2(cat);
                //這里的返回值不正確莽红,無法編譯
                return store2;
            }
        });
        //方式二:
        AsyncJob<AsyncJob<Uri>> storedUriAsyncJob2 = cutestCatAsyncJob.map(new Func<Cat, AsyncJob<Uri>>() {
        @Override
            public AsyncJob<Uri> call(Cat cat) {
                AsyncJob<Uri> uriAsyncJob = wrapper.store2(cat);
                //這里的返回值不符合要求
                return uriAsyncJob;
            }
       });          
       return storedUriAsyncJob;
}               
}

根據(jù)方式二妥畏,我們只能夠得到一個(gè)AsyncJob<AsyncJob>的雙層異步結(jié)果,看來我們需要把它再壓縮成一層AsyncJob才能滿足我們的需求安吁。在這里稱之為flatMap醉蚁。那么我們轉(zhuǎn)換的結(jié)果不再是R,而是AsyncJob<R>

public abstract class AsyncJob<T> {

    public abstract void start(Callback<T> callback);

    public <R> AsyncJob<R> map(final Func<T, R> func) {
        final AsyncJob<T> source = this;
        return new AsyncJob<R>() {
            @Override
            public void start(final Callback<R> callback) {
                source.start(new Callback<T>() {
                    @Override
                    public void onResult(T result) {
                        R mapped = func.call(result);
                        callback.onResult(mapped);
                    }

                    @Override
                    public void onError(Exception e) {
                        callback.onError(e);
                    }
                });
            }
        };
    }
    public <R> AsyncJob<R> flatMap(final Func<T, AsyncJob<R>> func) {
        final AsyncJob<T> source = this;
        return new AsyncJob<R>() {
            @Override
            public void start(final Callback<R> callback) {
                source.start(new Callback<T>() {
                    @Override
                    public void onResult(T result) {

                        AsyncJob<R> mapped = func.call(result);
                        mapped.start(new Callback<R>() {
                            @Override
                            public void onResult(R result) {
                                callback.onResult(result);
                            }

                            @Override
                            public void onError(Exception e) {
                                callback.onError(e);
                            }
                        });
                    }

                    @Override
                    public void onError(Exception e) {
                        callback.onError(e);
                    }
                });
            }
        };
    }
}

最后回到我們的CatsHelper類

public class CatsHelper {
    private ApiWrapper wrapper;
    public AsyncJob<Uri> saveTheCutestCat6(final String query) {
        final AsyncJob<List<Cat>> catListAsyncJob = wrapper.queryCats2(query);
        final AsyncJob<Cat> cutestCatAsyncJob = catListAsyncJob.map(new Func<List<Cat>, Cat>() {

            @Override
            public Cat call(List<Cat> catList) {
                return findCutestCat(catList);
            }
        });
        AsyncJob<Uri> uriAsyncJob = cutestCat.flatMap(new Func<Cat, AsyncJob<Uri>>() {
       
            @Override
            public AsyncJob<Uri> call(Cat cat) {
                return wrapper.store2(cat);
            }
        });
        return uriAsyncJob;
    }               
}

上面的代碼是不是似曾相識(shí)鬼店?是的网棍。回過頭再去看看前面我們說過的最理想的做法的代碼妇智±溺瑁可能不是很明顯氏身,我們用Java8表達(dá)式來看看

public class CatsHelper {
    private ApiWrapper wrapper;
    public AsyncJob<Uri> saveTheCutestCat7(final String query) {
        AsyncJob<List<Cat>> listAsyncJob = wrapper.queryCats2(query);
        AsyncJob<Cat> cutestCat = listAsyncJob.map(cats -> findCutestCat(cats));
        AsyncJob<Uri> uriAsyncJob = cutestCat.flatMap(cat -> wrapper.store2(cat));
        return uriAsyncJob;
    }
    private Cat findCutestCat(List<Cat> catList) {
        return Collections.max(catList);
    }
}

使用RxJava

public class RxJavaApiWrapper {
private Api api;        
        
    public Observable<List<Cat>> rxJavaQueryCats(String query) {
        return Observable.create(new Observable.OnSubscribe<List<Cat>>() {
            @Override
            public void call(Subscriber<? super List<Cat>> subscriber) {
                api.queryCats2(query, new Api.CatsQueryCallback() {
                    @Override
                    public void onCatListReceived(List<Cat> catList) {
                        subscriber.onNext(catList);
                    }

                    @Override
                    public void onError(Exception e) {
                        subscriber.onError(e);
                    }
                });
            }
        });
    }

    public Observable<Uri> rxJavaStore(Cat cat) {
        return Observable.create(new Observable.OnSubscribe<Uri>() {
            @Override
            public void call(Subscriber<? super Uri> subscriber) {
                api.store2(cat, new Api.StoreCallback() {
                    @Override
                    public void onCatStored(Uri uri) {
                        subscriber.onNext(uri);
                    }

                    @Override
                    public void onStoredFail(Exception e) {
                        subscriber.onError(e);
                    }
                });
            }
        });
    }

}
----------------------------------------------------------------------------

public class RxJavaCatsHelper {
    private ApiWrapper wrapper;
    public Observable<Uri> rxJavaSaveTheCutestCat(String query) {
        Observable<List<Cat>> listObservable = wrapper.rxJavaQueryCats(query);
        Observable<Cat> cutestCat = listObservable.map(new Func1<List<Cat>, Cat>() {
            @Override
            public Cat call(List<Cat> catList) {
                return findCutestCat(catList);
            }
        });
        Observable<Uri> uriObservable = cutestCat.flatMap(new Func1<Cat, Observable<Uri>>() {
            @Override
            public Observable<Uri> call(Cat cat) {
                return wrapper.rxJavaStore(cat);
            }
        });

        return uriObservable;
    }

    private Cat findCutestCat(List<Cat> catList) {
        return Collections.max(catList);
    }
}

總結(jié)

  • AsyncJob 等同于 Observable, 不僅僅可以返回一個(gè)結(jié)果惑畴,還可以返回一系列的結(jié)果蛋欣,當(dāng)然也可能沒有結(jié)果返回。

  • Callback 等同于 Observer如贷, 除了onNext(T t), onError(Throwable t)以外陷虎,還有一個(gè)onCompleted()函數(shù),該函數(shù)在結(jié)束繼續(xù)返回結(jié)果的時(shí)候通知Observable 杠袱。

  • abstract void start(Callback callback) 和 Subscription subscribe(final Observer observer) 類似尚猿,返回一個(gè)Subscription ,如果你不再需要后面的結(jié)果了霞掺,可以取消該任務(wù)谊路。

  • 除了 map 和 flatMap 以外, Observable 還有很多其他常見的轉(zhuǎn)換操作菩彬。

看看我們的代碼是不是和RxJava的代碼很相似缠劝。當(dāng)然我們的只是一個(gè)部分,RxJava還有很多好東西值得我們?nèi)ネ诰虻摹?/p>

參考資料

http://yarikx.github.io/NotRxJava/

http://blog.chengyunfeng.com/?p=729

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市骗灶,隨后出現(xiàn)的幾起案子惨恭,更是在濱河造成了極大的恐慌,老刑警劉巖耙旦,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脱羡,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡免都,警方通過查閱死者的電腦和手機(jī)锉罐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绕娘,“玉大人脓规,你說我怎么就攤上這事∠樟欤” “怎么了侨舆?”我有些...
    開封第一講書人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長绢陌。 經(jīng)常有香客問我挨下,道長,這世上最難降的妖魔是什么脐湾? 我笑而不...
    開封第一講書人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任臭笆,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘耗啦。我一直安慰自己凿菩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開白布帜讲。 她就那樣靜靜地躺著,像睡著了一般椒拗。 火紅的嫁衣襯著肌膚如雪似将。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,208評(píng)論 1 299
  • 那天蚀苛,我揣著相機(jī)與錄音在验,去河邊找鬼。 笑死堵未,一個(gè)胖子當(dāng)著我的面吹牛腋舌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播渗蟹,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼块饺,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了雌芽?” 一聲冷哼從身側(cè)響起授艰,我...
    開封第一講書人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎世落,沒想到半個(gè)月后淮腾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡屉佳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年谷朝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片武花。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡圆凰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出髓堪,到底是詐尸還是另有隱情送朱,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布干旁,位于F島的核電站驶沼,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏争群。R本人自食惡果不足惜回怜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望查蓉。 院中可真熱鬧踢京,春花似錦、人聲如沸蚁吝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至驹碍,卻和暖如春壁涎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背志秃。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來泰國打工怔球, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人浮还。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓竟坛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親钧舌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子担汤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

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