Android拾萃 - 從零打造一個RxJava(搞清楚RxJava代碼為什么這么寫)

任何框架都是從無到有退疫,都是為了解決問題而產(chǎn)生,那么RxJava是如何產(chǎn)生的呢勾哩?RxJava代碼的寫法抗蠢,為何如此讓人看不懂,回調(diào)的參數(shù)等等思劳,讓小白看了摸不著頭腦迅矛。

接下來的文章,主要是依據(jù)NotRxJava懶人專用指南,結(jié)合自己的理解潜叛,寫的一篇更加小白的文章秽褒,以幫助我們更好的梳理和理解。

Cat 應(yīng)用程序

讓我們來創(chuàng)建一個真實世界的例子威兜。我們都知道貓是我們技術(shù)發(fā)展的引擎销斟,所以就讓我們也來創(chuàng)建這么一個用來下載貓圖片的典型應(yīng)用吧。

任務(wù)描述

我們有個 Web API椒舵,能根據(jù)給定的查詢請求搜索到整個互聯(lián)網(wǎng)上貓的圖片蚂踊。每個圖片包含可愛指數(shù)的參數(shù)(描述圖片可愛度的整型值)。我們的任務(wù)將會下載到一個貓列表的集合笔宿,選擇最可愛的那個犁钟,然后把它保存到本地。
我們只關(guān)心下載泼橘、處理和保存貓的數(shù)據(jù)涝动。

我們開始吧~

思維導(dǎo)圖

這里各個點(diǎn)后面都會結(jié)合這個圖,進(jìn)行解說

貓程序.png
模型和 API

根據(jù)我們的任務(wù)炬灭,我們能夠得到的信息

  1. 給定的查詢信息String query
  2. 搜索所有的貓圖片 List<Cat> queryCats(String query)
  3. 每個圖片包含可愛指數(shù)的參數(shù)(描述圖片可愛度的整型值)找到最可愛的貓 (Cat 包含圖片Bitmap image和可愛屬性int cuteness)醋粟。
  4. 最可愛的貓圖片保存到本地(Uri store(Cat cat),不清楚Uri 的自行查閱)

“貓”的數(shù)據(jù)結(jié)構(gòu):

public class Cat implements Comparable<Cat> {
    //圖片
    Bitmap image;
    //可愛屬性
    int cuteness;

    //為了實現(xiàn)比較功能,我們的Cat對象需要實現(xiàn)Comparable<Cat>接口昔穴,然后重寫compareTo方法
    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    @Override
    public int compareTo(@NonNull Cat another) {
        //這里大小比較只針對可愛屬性  cuteness
        return Integer.compare(cuteness, another.cuteness);
    }
}

我們先不考慮異步情況镰官,那么我們的接口Api:

public interface Api {
    //阻塞
    List<Cat> queryCats(String query);
    Uri store(Cat cat);
}

然后吧我們的業(yè)務(wù)邏輯封裝到我們的helper類


public class CatsHelper {
    Api api;

    //組合方法
    public Uri saveTheCutestCat(String query){
        List<Cat> cats = api.queryCats(query);
        Cat cat = findCutestCat(cats);
        Uri uri = api.store(cat);
        return uri;
    }

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

我們知道,在實際應(yīng)用之中吗货,我們會處理很多任務(wù)泳唠,如果使用上面的阻塞方式,用戶的每次操作都需要等待返回之后才能繼續(xù)宙搬,這明顯是不好的笨腥,因此我們需要異步

異步Api接口

我們queryCats是一個http請求勇垛,搜索完畢脖母,需要有一個回調(diào)告訴我們,以讓我們處理數(shù)據(jù)闲孤,并進(jìn)行下步業(yè)務(wù)邏輯谆级,于是,新的api的代碼如下:

public interface Api {
    //阻塞api
    List<Cat> queryCats(String query);
    Uri store(Cat cat);

    //異步api讼积,這里就不需要返回值了肥照,直接在回調(diào)里獲取即可
    void queryCatsAsync(String query, CatsQueryCallback callback);
    void store(Cat cat, StoreCallback storeCallback);

//請求的接口回調(diào)
    interface CatsQueryCallback {
        void onCatListReceived(List<Cat> cats);
        void onError(Exception e);
    }

//保存數(shù)據(jù)的接口回調(diào)
    interface StoreCallback{
        void onCatStored(Uri uri);
        void onStoreFailed(Exception e);
    }
}

我們的helper類,作出相應(yīng)的更改勤众,為了讓異步更加完整舆绎,我們比較最可愛的貓成功或者失敗,都需要告訴用戶们颜,于是也需要寫一個接口CutestCatCallback回調(diào)出去吕朵。


public class CatsHelper {

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

    //監(jiān)聽接口
    public interface CutestCatCallback {
        void onCutestCatSaved(Uri uri);
        void onQueryFailed(Exception e);
    }

    Api api;

    //組合方法
    public Uri saveTheCutestCat(String query){
        List<Cat> cats = api.queryCats(query);
        Cat cat = findCutestCat(cats);
        Uri uri = api.store(cat);
        return uri;findCutestCat
    }

    //異步
    public  void saveTheCutestCatAsyc(final String query, final CutestCatCallback cutestCatCallback){
        api.queryCatsAsync(query, new Api.CatsQueryCallback() {
            @Override
            public void onCatListReceived(List<Cat> cats) {
                Cat cat = findCutestCat(cats);
                api.store(cat, new Api.StoreCallback() {
                    @Override
                    public void onCatStored(Uri uri) {
                        cutestCatCallback.onCutestCatSaved(uri);
                    }

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

            }

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

}

現(xiàn)在它有了更多無關(guān)代碼和花括號,但是邏輯是一樣的窥突。
每一個異步操作努溃,我們都必須創(chuàng)建出回調(diào)接口并在代碼中手動的插入它們,接口也越來越多波岛!
在這樣的代碼中錯誤不會自動地傳遞茅坛,我們需要在手動在onStoreFailed和onQueryFailed方法里面,添加代碼则拷,將錯誤傳遞給上一層贡蓖。

為了解決這些問題,我們引入了泛型回調(diào)

泛型回調(diào)
泛型類
/**
 * 定義泛型類
 *
 * 與普通類的定義相比煌茬,上面的代碼在類名后面多出了 <T1, T2>斥铺,
 * T1, T2 是自定義的標(biāo)識符,也是參數(shù)坛善,用來傳遞數(shù)據(jù)的類型晾蜘,而不是數(shù)據(jù)的值邻眷,我們稱之為類型參數(shù)。
 * 在泛型中剔交,不但數(shù)據(jù)的值可以通過參數(shù)傳遞肆饶,數(shù)據(jù)的類型也可以通過參數(shù)傳遞。
 * T1, T2 只是數(shù)據(jù)類型的占位符岖常,運(yùn)行時會被替換為真正的數(shù)據(jù)類型驯镊。
 * Created by philos on 17-9-16.
 */
public class Point<T1, T2>{
    T1 x;
    T2 y;
    public T1 getX() {
        return x;
    }
    public void setX(T1 x) {
        this.x = x;
    }
    public T2 getY() {
        return y;
    }
    public void setY(T2 y) {
        this.y = y;
    }
}
泛型接口
/**
 * 定義泛型接口
 * Created by philos on 17-9-16.
 */

interface Info<T> {
    public T getVar();
}

接口實現(xiàn)類


/**
 * 實現(xiàn)泛型接口
 * Created by philos on 17-9-16.
 */

public class InfoImp<T> implements Info<T> {

    private T var;

    // 定義泛型構(gòu)造方法
    public InfoImp(T var) {
        this.setVar(var);
    }
    public void setVar(T var) {
        this.var = var;
    }

    @Override
    public T getVar() {
        return this.var;
    }
}
泛型方法(固定參數(shù) 和 可變參數(shù))
public class Main {
    //泛型方法
    public static <T> void out(T t) {
        System.out.println(t);
    }

    //可變參數(shù) 泛型方法
    public static <T> void out(T... args) {
        for (T t : args) {
            System.out.println(t);
        }
    }

    public static void main(String[] args) {
        //普通泛型
        out("findingsea");
        out(123);
        out(11.11);
        out(true);

        //可變參數(shù)泛型
        out("findingsea", 123, 11.11, true);
    }
}

了解了上面具體的寫法,我們繼續(xù)改造我們的貓程序代碼竭鞍,請看上面的思維導(dǎo)圖左邊的異步優(yōu)化板惑。

泛型回調(diào)

對比接口可以發(fā)現(xiàn),回調(diào)情況有兩種偎快,成功和失敗冯乘,成功的返回一個對象,失敗的返回Exception類晒夹,于是新增統(tǒng)一的回調(diào)接口如下:

/**
 * 泛型回調(diào)
 * 找到共同的模式裆馒,進(jìn)行抽離
 * 替代原來所有的接口
 * Created by philos on 17-9-15.
 */

public interface Callback<T> {
    void onResult(T result);
    void onError(Exception e);
}

Api 包裝類ApiWrapper

我們的之前就定義完畢了,上線之后惋戏,一般不會進(jìn)行大幅度修改领追,這個時候,我們可以實現(xiàn)一個Api包裝類响逢。

/**
 * 使用了泛型之后,兩個接口合并為一個接口
 * Created by philos on 17-9-15.
 */

public class ApiWrapper {
    Api api;

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

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

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

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

我們對應(yīng)的helper類就不在持有Api對象了棕孙,換成了包裝類舔亭,代碼修改如下:

public class CatsHelper {
    private Cat findCutestCat(List<Cat> cats) {
        return Collections.max(cats);
    }

    //監(jiān)聽接口
    public interface CutestCatCallback {
        void onCutestCatSaved(Uri uri);
        void onQueryFailed(Exception e);
    }

    //泛型回調(diào) (retrofit很像有木有)
    ApiWrapper apiWrapper;

    public  void saveTheCutestCatWrap(final String query, final CutestCatCallback cutestCatCallback){
        apiWrapper.queryCats(query, new Callback<List<Cat>>() {
            @Override
            public void onResult(List<Cat> result) {
                Cat cat = findCutestCat(result);
                apiWrapper.store(cat, new Callback<Uri>() {
                    @Override
                    public void onResult(Uri result) {
                        cutestCatCallback.onCutestCatSaved(result);
                    }

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

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

}

分解,使用臨時泛型對象

我們發(fā)現(xiàn)異步操作(queryCats蟀俊,queryCats钦铺,還有saveTheCutestCat),它們都遵循了相同的模式肢预。調(diào)用它們的方法有一些參數(shù)(query矛洞、cat)也包括一個回調(diào)對象。再次說明:任何異步操作需要攜帶所需的常規(guī)參數(shù)和一個回調(diào)實例對象烫映。請看思維導(dǎo)圖左邊異步優(yōu)化沼本。

臨時泛型對象

對外提供的方法名暫時起為start

/**
 * 分解異步操作,每步有一個參數(shù)
 * 每步返回锭沟,臨時對象
 * 每步攜帶 回調(diào)信息
 * 避免回調(diào)地獄抽兆,用鏈?zhǔn)浇鉀Q問題
 * Created by philos on 17-9-16.
 */

public abstract class AsyncJob<T> {
    //包裝了方法,對外隱藏族淮,外部只需傳入回調(diào)辫红,定義返回類型凭涂,調(diào)用是start即可
    public abstract void start(Callback<T> callback);
}

改造wrapper類,把我們以前的方法也隱藏起來了


/**
 * 所有的異步操作都統(tǒng)一了
 * 每次異步操作都返回AsyncJob<T>
 * 每次異步都攜帶 回調(diào)Callback<T>
 * start包裝了真正的請求贴妻,外部不關(guān)心切油,只要調(diào)用start即可
 * Created by philos on 17-9-16.
 */
public class ApiWrapper {
    Api api;

    public AsyncJob<List<Cat>> queryCats(final String query){
        //返回臨時對象
        return new AsyncJob<List<Cat>>() {
            @Override
            public void start(final Callback<List<Cat>> callback) {
                //這里進(jìn)行請求,然后返回給callback就可以了
                api.queryCatsAsync(query, new Api.CatsQueryCallback() {
                    @Override
                    public void onCatListReceived(List<Cat> cats) {
                        callback.onResult(cats);
                    }

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


    public AsyncJob<Uri> store(final Cat cat){
        return new AsyncJob<Uri>() {
            @Override
            public void start(final Callback<Uri> callback) {
                //進(jìn)行請求名惩,然后返回給callback
                api.store(cat, new Api.StoreCallback() {
                    @Override
                    public void onCatStored(Uri uri) {
                        callback.onResult(uri);
                    }

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

helper類也需要進(jìn)行修改

public class CatsHelper {

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

    //監(jiān)聽接口
    public interface CutestCatCallback {
        void onCutestCatSaved(Uri uri);
        void onQueryFailed(Exception e);
    }
    //分解統(tǒng)一返回臨時對象,鏈?zhǔn)秸{(diào)用澎胡,高大上有木有

    ApiWrapper apiWrapper;

    public AsyncJob<Uri> saveTheCutestCat(final String query){
        //直接new 需要返回的對象
        return new AsyncJob<Uri>() {
            @Override
            public void start(final Callback<Uri> callback) {
                //這里請求數(shù)據(jù),返回,  每個異步都有自己的回調(diào)信息這里重新new 一個CallBack
                apiWrapper.queryCats(query).start(new Callback<List<Cat>>() {
                    @Override
                    public void onResult(List<Cat> result) {
                        //進(jìn)行數(shù)據(jù)操作和轉(zhuǎn)換
                        Cat cat = findCutestCat(result);
                        apiWrapper.store(cat).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);
                    }
                });
            }
        };
    }
}

是我們的邏輯數(shù)據(jù)流:

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

分解成更小的操作


public class CatsHelper {

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

    //監(jiān)聽接口
    public interface CutestCatCallback {
        void onCutestCatSaved(Uri uri);
        void onQueryFailed(Exception e);
    }

    ApiWrapper apiWrapper;

    public AsyncJob<Uri> saveTheCutestCat(String query) {
        //獲取貓列表  返回臨時對象  catsListAsyncJob
        final AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper.queryCats(query);

        //查找最可愛貓  返回臨時對象  cutestCatAsyncJob
        final AsyncJob<Cat> cutestCatAsyncJob = new AsyncJob<Cat>() {
            @Override
            public void start(final Callback<Cat> callback) {
                catsListAsyncJob.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);
                    }
                });
            }
        };

        //保存貓到本地  返回臨時對象  storedUriAsyncJob
        AsyncJob<Uri> storedUriAsyncJob = new AsyncJob<Uri>() {
            @Override
            public void start(final Callback<Uri> cutestCatCallback) {
                cutestCatAsyncJob.start(new Callback<Cat>() {
                    @Override
                    public void onResult(Cat cutest) {
                        apiWrapper.store(cutest)
                                .start(new Callback<Uri>() {
                                    @Override
                                    public void onResult(Uri result) {
                                        cutestCatCallback.onResult(result);
                                    }

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

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

映射 (其實就是操作符的封裝)

現(xiàn)在看看 AsyncJob<Cat> cutestCatAsyncJob的部分:

AsyncJob<Cat> cutestCatAsyncJob = new AsyncJob<Cat>() {
            @Override
            public void start(Callback<Cat> callback) {
                catsListAsyncJob.start(new Callback<List<Cat>>() {
                    @Override
                    public void onResult(List<Cat> result) {
                        callback.onResult(findCutest(result));
                    }
 
                    @Override
                    public void onError(Exception e) {
                        callback.onError(e);
                    }
                });
            }
        };

這 16 行代碼只有一行是對我們有用(對于邏輯來說)的操作:

findCutest(result)

剩下的僅僅是開啟另外一個AsyncJob和傳遞結(jié)果與錯誤的樣板代碼绢片。此外滤馍,這些代碼并不用于特定的任務(wù),我們可以把其移動到其它地方而不影響編寫我們真正需要的業(yè)務(wù)代碼底循。

我們要怎么做呢巢株?

將方法findCutest分離出來,并和前面一樣返回臨時對象熙涤,供下步使用阁苞。

在 Java 中不能直接傳遞方法(函數(shù))所以我們需要通過類(和接口)來間接實現(xiàn)這樣的功能,我們來定義轉(zhuǎn)換方法的接口:

轉(zhuǎn)換方法的接口
/**
 * 方法傳入接口
 * T對應(yīng)于參數(shù)類型而R對應(yīng)于返回類型
 * Created by philos on 17-9-16.
 */

public interface Func<T, R> {
    R call(T t);
}

我們之前返回的臨時對象的start方法是通過回調(diào)返回的祠挫,但是現(xiàn)在是調(diào)用方法轉(zhuǎn)換需要返回臨時對象那槽,接著上面繼續(xù)看怎么改造。
我們新增一個方法等舔,就叫map骚灸,返回對象R,參考上面泛型方法慌植,AsyncJob代碼修改如下:

public abstract class AsyncJob<T> {
    //包裝了方法甚牲,對外隱藏,外部只需傳入回調(diào)蝶柿,定義返回類型丈钙,調(diào)用是start即可
    public abstract void start(Callback<T> callback);
//返回臨時對象 方法(需要傳入映射的方法Func<T, R> func攜帶的是對象R),
    public <R> AsyncJob<R> map(final Func<T, R> func){
        //使用自己交汤,為鏈?zhǔn)秸{(diào)用返回同樣的對象(return 一個新建的)
        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) {
                        //方法 替換成func
                        //返回對象為R
                        R mapped = func.call(result);
                        callback.onResult(mapped);
                    }

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

前面map對應(yīng)的是分離一個方法返回的是實體對象的情況雏赦,那么如果是需要返回同樣的臨時對象的方法,比如上面的CatsHelper 的storedUriAsyncJob嵌套了兩個start方法芙扎,所以AsyncJob在添加一個方法星岗,就叫做flatMap


    //返回臨時對象 方法(需要傳入映射的方法Func<T, AsyncJob<R>> func攜帶的是上一個臨時對象AsyncJob<R>)
    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(final T result) {
                        //傳入的方法不一樣,返回值不一樣纵顾,在這里有區(qū)別
                        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);
                    }
                });
            }
        };
    }

這里我們可以簡單的理解伍茄,我們之前把回調(diào)用start表示,但是對于start還調(diào)用了其他方法的施逾,還是存在嵌套敷矫,那么我們的map就是為了解決這個問題的例获。但是如果是多次start的嵌套,map明顯不滿足曹仗,這個時候我們使用flatMap 榨汤。

于是,我們的helper代碼修改如下:


public class CatsHelper {

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

    //監(jiān)聽接口
    public interface CutestCatCallback {
        void onCutestCatSaved(Uri uri);
        void onQueryFailed(Exception e);
    }
  //映射
    public AsyncJob<Uri> saveTheCutestCat4(String query){
        //獲取貓列表  返回臨時對象  catsListAsyncJob
        final AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper2.queryCats(query);

        //查找最可愛貓  返回臨時對象  cutestCatAsyncJob
        //這里用Func將方法findCutestCat(result)分離出來怎茫,并返回一個臨時對象cutestCatAsyncJob
        final AsyncJob<Cat> cutestCatAsyncJob = catsListAsyncJob.map(new Func<List<Cat>, Cat>() {
            @Override
            public Cat call(List<Cat> cats) {
                return findCutestCat(cats);
            }
        });

        //保存貓到本地  返回臨時對象  storedUriAsyncJob
        AsyncJob<AsyncJob<Uri>> storedUriAsyncJob = cutestCatAsyncJob.map(new Func<Cat, AsyncJob<Uri>>() {
            @Override
            public AsyncJob<Uri> call(Cat cat) {
                return apiWrapper2.store(cat);
            }
        });
        return storedUriAsyncJob;
    }
}

RxJava

嘿收壕,你不需要把那些代碼拷到你的項目中,因為我們還是實現(xiàn)地不夠完全的轨蛤,僅僅算是非線程安全的 RxJava 的一小部分而已蜜宪。
它們之間只有一些差異:
AsyncJob<T>
就是實際上的 Observable
,它不僅可以只分發(fā)一個單一的結(jié)果也可以是一個序列(可以為空)祥山。

Callback<T>
就是 Observer
圃验,除了 Callback 少了onNext(T t)
方法。Observer 中在onError(Throwable t)
方法被調(diào)用后缝呕,會繼而調(diào)用onCompleted()
澳窑,然后 Observer 會包裝好并發(fā)送出事件流(因為它能發(fā)送一個序列)。

abstract void start(Callback<T> callback)
對應(yīng) Subscription subscribe(final Observer<? super T> observer)供常,這個方法也返回 Subscription 摊聋,在不需要它時你可以決定取消接收事件流。

除了map
和flatMap
方法栈暇,Observable
在 Observalbes 之上也有一些其它有用的操作麻裁。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市源祈,隨后出現(xiàn)的幾起案子悲立,更是在濱河造成了極大的恐慌,老刑警劉巖新博,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異脚草,居然都是意外死亡赫悄,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門馏慨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來埂淮,“玉大人,你說我怎么就攤上這事写隶【笞玻” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵慕趴,是天一觀的道長痪蝇。 經(jīng)常有香客問我鄙陡,道長,這世上最難降的妖魔是什么躏啰? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任趁矾,我火速辦了婚禮,結(jié)果婚禮上给僵,老公的妹妹穿的比我還像新娘毫捣。我一直安慰自己,他們只是感情好帝际,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布蔓同。 她就那樣靜靜地躺著,像睡著了一般蹲诀。 火紅的嫁衣襯著肌膚如雪斑粱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天侧甫,我揣著相機(jī)與錄音珊佣,去河邊找鬼。 笑死披粟,一個胖子當(dāng)著我的面吹牛咒锻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播守屉,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼惑艇,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了拇泛?” 一聲冷哼從身側(cè)響起滨巴,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎俺叭,沒想到半個月后恭取,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡熄守,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年蜈垮,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片裕照。...
    茶點(diǎn)故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡攒发,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出晋南,到底是詐尸還是另有隱情惠猿,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布负间,位于F島的核電站偶妖,受9級特大地震影響姜凄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜餐屎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一檀葛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧腹缩,春花似錦屿聋、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽怔接。三九已至骂维,卻和暖如春侠畔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背竿痰。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工脆粥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人影涉。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓变隔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蟹倾。 傳聞我的和親對象是個殘疾皇子匣缘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評論 2 345

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

  • 一、簡歷準(zhǔn)備 1鲜棠、個人技能 (1)自定義控件肌厨、UI設(shè)計、常用動畫特效 自定義控件 ①為什么要自定義控件豁陆? Andr...
    lucas777閱讀 5,187評論 2 54
  • RxJava詳解(二) 說好的簡潔呢柑爸? 上面這一部分,又是介紹盒音、又是Hello World竖配、又是數(shù)據(jù)變換,但是你會...
    CharonChui閱讀 318評論 0 0
  • 6月用爪,桃子的季節(jié)原押。每到這個時候,我就想起了奶奶偎血,還有奶奶門前那顆桃子樹诸衔。 桃子樹不高盯漂,卻有非常多的枝丫。春天的時候...
    老衲兮閱讀 182評論 0 0
  • https://www.shaketheskycasino.com/mobile/index.php
    必應(yīng)2016閱讀 317評論 0 0
  • 生命的起點(diǎn)與終點(diǎn)笨农,僅僅源于瞬間就缆,人若浮塵游走于天地間,看盡世間萬象谒亦,歷盡塵世繁俗竭宰,驀然回首:人不在,心在份招;心不在切揭,...
    斜陽脈脈水悠悠閱讀 129評論 0 3