Retrofit基本使用

上一章节讲解了OkHttp的基本使用,这节就来看看Retrofit的基本使用
调用的网址如下:

    public static final String RETROFIT_BASE_URL = "https://api.douban.com/";
    public static final String POS_HTTP_WEATHER = "http://api.k780.com:88/";
    public static final String URL_IMAGE_01 = "http://d.hiphotos.baidu.com/";
Retrofit的单独使用
  • GETQuery的使用
    @GET("/v2/book/search?")
    Call<BookInfo> getDuBanDataUseQuery(@Query("q") String bookName, @Query("tag") String tag, @Query("start") int start, @Query("count") int count);

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(UrlUtils.RETROFIT_BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        RetrofitApi retrofitApi = retrofit.create(RetrofitApi.class);
        Call<BookInfo> duBanDataCall = retrofitApi.getDuBanDataUseQuery("小王子", null, 0, 3);
        duBanDataCall.enqueue(new Callback<BookInfo>() {
            @Override
            public void onResponse(Call<BookInfo> call, Response<BookInfo> response) {
                if (response != null && response.body() != null) {
                    List<Book> results = response.body().getBooks();
                    for (Book book : results) {
                        Log.e("LHC", "bookTitle:" + book.getTitle() + ", bookAuthor:" + book.getAuthor()[0]);
                    }
                }
            }

            @Override
            public void onFailure(Call<BookInfo> call, Throwable t) {
                ToastUtils.showShort("调用失败");
                t.printStackTrace();
            }
        });
  • GETQueryMap的使用
    @GET("v2/book/search?")
    Call<BookInfo> getDuBanDataUseQueryMap(@QueryMap Map<String, String> options);

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(UrlUtils.RETROFIT_BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        RetrofitApi retrofitApi = retrofit.create(RetrofitApi.class);

        Map<String, String> options = new HashMap<>();
        options.put("q", "小王子");
        options.put("tag", "");
        options.put("start", "0");
        options.put("count", "3");
        Call<BookInfo>  duBanDataCall = retrofitApi.getDuBanDataUseQueryMap(options);
        duBanDataCall.enqueue(new Callback<BookInfo>() {
            @Override
            public void onResponse(Call<BookInfo> call, Response<BookInfo> response) {
                if (response != null && response.body() != null) {
                    List<Book> results = response.body().getBooks();
                    for (Book book : results) {
                        Log.e("LHC", "bookTitle:" + book.getTitle() + ", bookAuthor:" + book.getAuthor()[0]);
                    }
                }
            }

            @Override
            public void onFailure(Call<BookInfo> call, Throwable t) {
                ToastUtils.showShort("调用失败");
                t.printStackTrace();
            }
        });
  • GETPath的使用
    @GET("v2/book/{id}")
    Call<Book> getDuBanDataUsePath(@Path("id") String id);

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(UrlUtils.RETROFIT_BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        RetrofitApi retrofitApi = retrofit.create(RetrofitApi.class);
        Call<Book> duBanDataUsePathCall = retrofitApi.getDuBanDataUsePath("1003078");
        duBanDataUsePathCall.enqueue(new Callback<Book>() {
            @Override
            public void onResponse(Call<Book> call, Response<Book> response) {
                if (response != null && response.body() != null) {
                    Book book = response.body();
                    Log.e("LHC", "bookTitle:" + book.getTitle() + ", bookAuthor:" + book.getAuthor()[0]);
                }
            }

            @Override
            public void onFailure(Call<Book> call, Throwable t) {
                ToastUtils.showShort("调用失败");
                t.printStackTrace();
            }
        });
  • POSTField的使用
    @FormUrlEncoded
    @POST("/?")
    Call<Weathers> getWeathersData(@Field("app") String app, @Field("weaid") String weaId, @Field("appkey") String appKey, @Field("sign") String sign, @Field("format") String format);

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(UrlUtils.POS_HTTP_WEATHER)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        RetrofitApi retrofitApi = retrofit.create(RetrofitApi.class);
        Call<Weathers> weathersDataCall = retrofitApi.getWeathersData("weather.future", "1", "10003", "b59bc3ef6191eb9f747dd4e83c99f2a4", "json");
        weathersDataCall.enqueue(new Callback<Weathers>() {
            @Override
            public void onResponse(Call<Weathers> call, Response<Weathers> response) {
                if (response != null && response.body() != null) {
                    List<WeatherInfo> results = response.body().getResult();
                    for (WeatherInfo weatherInfo : results) {
                        Log.e("LHC", "cityName:" + weatherInfo.getCitynm() + ", days:" + weatherInfo.getDays() + ", temperature:" + weatherInfo.getTemperature());
                    }
                }
            }

            @Override
            public void onFailure(Call<Weathers> call, Throwable t) {
                ToastUtils.showShort("调用失败");
            }
        });
  • POSTFieldMap的使用
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(UrlUtils.POS_HTTP_WEATHER)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        RetrofitApi retrofitApi = retrofit.create(RetrofitApi.class);
            Map<String, String> options = new HashMap<>();
            options.put("app", "weather.future");
            options.put("weaid", "1");
            options.put("appkey", "10003");
            options.put("sign", "b59bc3ef6191eb9f747dd4e83c99f2a4");
            options.put("format", "json");
            Call<Weathers> weathersDataCall = retrofitApi.getWeathersData(options);
        weathersDataCall.enqueue(new Callback<Weathers>() {
            @Override
            public void onResponse(Call<Weathers> call, Response<Weathers> response) {
                if (response != null && response.body() != null) {
                    List<WeatherInfo> results = response.body().getResult();
                    for (WeatherInfo weatherInfo : results) {
                        Log.e("LHC", "cityName:" + weatherInfo.getCitynm() + ", days:" + weatherInfo.getDays() + ", temperature:" + weatherInfo.getTemperature());
                    }
                }
            }

            @Override
            public void onFailure(Call<Weathers> call, Throwable t) {
                ToastUtils.showShort("调用失败");
            }
        });
  • POSTBody的使用
//没有调用成功
  • 下载文件
    @Streaming
    @GET("zhidao/pic/item/0bd162d9f2d3572c5a387f618c13632763d0c3b1.jpg")
    Call<ResponseBody> downFile();

    private String FILE_DIR = Environment.getExternalStorageDirectory() + File.separator + "downImage";
    private String FILE_NAME = "ok_" + System.currentTimeMillis() + ".jpg";

    private void downLoadImage() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(UrlUtils.URL_IMAGE_01)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        RetrofitApi retrofitApi = retrofit.create(RetrofitApi.class);
        Call<ResponseBody> call = retrofitApi.downFile();
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, final Response<ResponseBody> response) {
                if (response != null && response.body() != null) {
                    ThreadUtil.getInstance().getExecutorService().execute(new Runnable() {
                        @Override
                        public void run() {
                            writeFileToDisk(response);
                        }
                    });
                }
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                t.printStackTrace();
            }
        });
    }

    private void writeFileToDisk(Response<ResponseBody> response) {
        InputStream is = null;
        FileOutputStream fos = null;
        try {
            byte[] buff = new byte[2048];
            is = response.body().byteStream();
            File filePath = new File(FILE_DIR);
            if (!filePath.mkdirs()) {
                filePath.createNewFile();
            }
            final File file = new File(filePath, FILE_NAME);
            fos = new FileOutputStream(file);
            int len = 0;
            while ((len = is.read(buff)) != -1) {
                fos.write(buff, 0, len);
            }
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    Glide.with(RetrofitMainActivity.this).load(file.getAbsoluteFile()).into(ivShow);
                }
            });
            fos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
  • 上传文件
    @Multipart
    @POST("xxx/uploadImage")
    Call<FileBean> upLoadMultiFile1(@Part MultipartBody.Part[] parts);

    private void upLoadMultiFile1() {
        List<File> files = getFiles();
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(UrlUtils.URL_UPLOAD_BASE)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        RetrofitApi retrofitApi = retrofit.create(RetrofitApi.class);
        MultipartBody.Part[] parts = new MultipartBody.Part[files.size()];
        for (int i = 0; i < files.size(); i++) {
            File file = files.get(i);
            RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
            MultipartBody.Part filePart = MultipartBody.Part.createFormData(file.getName(), requestFile);
            parts[i] = filePart;
        }

        Call<FileBean> fileBeanCall = retrofitApi.upLoadMultiFile1("files", parts);
        fileBeanCall.enqueue(new Callback<FileBean>() {
            @Override
            public void onResponse(Call<FileBean> call, Response<FileBean> response) {
                List<String> result = response.body().getResult();
                for (String s : result) {
                    Log.e("LHC", "imgUrl:\n"+ s);
                }
            }

            @Override
            public void onFailure(Call<FileBean> call, Throwable t) {
                t.printStackTrace();
            }
        });
    }

    private List<File> getFiles() {
        List<File> files = new ArrayList<>();
        files.add(new File(Environment.getExternalStorageDirectory()+File.separator+"crop_image.jpg"));
        files.add(new File(Environment.getExternalStorageDirectory()+File.separator+"temp.png"));
        return files;
    }
Retrofit+RxJava联合使用
  • GETQueryMap的使用
    @GET("v2/book/search?")
    Observable<BookInfo> getDuBanDataUseQueryMap1(@QueryMap Map<String, String> options);

    private void getQueryMap() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(UrlUtils.RETROFIT_BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();
        RetrofitApi retrofitApi = retrofit.create(RetrofitApi.class);

        Map<String, String> options = new HashMap<>();
        options.put("q", "小王子");
        options.put("tag", "");
        options.put("start", "0");
        options.put("count", "3");
        Observable<BookInfo> duBanDataUseQueryMap = retrofitApi.getDuBanDataUseQueryMap1(options);
        duBanDataUseQueryMap.observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io()).subscribe(new Observer<BookInfo>() {
            @Override
            public void onCompleted() {
                ToastUtils.showShort("调用成功");
            }

            @Override
            public void onError(Throwable e) {
                ToastUtils.showShort("调用失败");
                e.printStackTrace();
            }

            @Override
            public void onNext(BookInfo bookInfo) {
                List<Book> results = bookInfo.getBooks();
                for (Book book : results) {
                    Log.e("LHC", "bookTitle:" + book.getTitle() + ", bookAuthor:" + book.getAuthor()[0]);
                }
            }
        });
    }
  • POSTFieldMap的使用
    @FormUrlEncoded
    @POST("/?")
    Observable<Weathers> getWeathersData1(@FieldMap Map<String, String> options);

    private void postFieldMap() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(UrlUtils.POS_HTTP_WEATHER)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();
        RetrofitApi retrofitApi = retrofit.create(RetrofitApi.class);

        Map<String, String> options = new HashMap<>();
        options.put("app", "weather.future");
        options.put("weaid", "1");
        options.put("appkey", "10003");
        options.put("sign", "b59bc3ef6191eb9f747dd4e83c99f2a4");
        options.put("format", "json");
        Observable<Weathers> duBanDataUseQueryMap = retrofitApi.getWeathersData1(options);
        duBanDataUseQueryMap.observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io()).subscribe(new Observer<Weathers>() {
            @Override
            public void onCompleted() {
                ToastUtils.showShort("调用成功");
            }

            @Override
            public void onError(Throwable e) {
                ToastUtils.showShort("调用失败");
                e.printStackTrace();
            }

            @Override
            public void onNext(Weathers bookInfo) {
                List<WeatherInfo> results = bookInfo.getResult();
                for (WeatherInfo weatherInfo : results) {
                    Log.e("LHC", "cityName:" + weatherInfo.getCitynm() + ", days:" + weatherInfo.getDays() + ", temperature:" + weatherInfo.getTemperature());
                }
            }
        });
    }
  • 下载文件
//    @Streaming //单独使用`Retrofit`时,加上此参数并没有报错,但是和`RxJava`联合起来使用时,会出现错误
    @GET("zhidao/pic/item/0bd162d9f2d3572c5a387f618c13632763d0c3b1.jpg")
    Observable<ResponseBody> downFile1();

    private String FILE_DIR = Environment.getExternalStorageDirectory() + File.separator + "downImage";
    private String FILE_NAME = "ok_" + System.currentTimeMillis() + ".jpg";
    private void downImage() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(UrlUtils.URL_IMAGE_01)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();
        RetrofitApi retrofitApi = retrofit.create(RetrofitApi.class);
        Observable<ResponseBody> observable = retrofitApi.downFile1();
        observable.observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe(new Observer<ResponseBody>() {
                    @Override
                    public void onCompleted() {
                        Log.e("LHC", "下载成功");
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.e("LHC", "下载失败");
                        e.printStackTrace();
                    }

                    @Override
                    public void onNext(final ResponseBody responseBody) {
                        mHandler = ThreadUtil.getInstance().getHandler();
                        ThreadUtil.getInstance().getExecutorService().execute(new Runnable() {
                            @Override
                            public void run() {
                                writeFileToDisk(responseBody);//
                            }
                        });
                    }
                });
    }

从上面可以看到,RetrofitRetrofit+RxJava的使用差不多,不用在与接口返回值和回调方法。所以这里就只列举这几个。

Retrofit+RxJava的简单封装
public class RetrofitUtils {
    private static RetrofitUtils mRetrofitUtils;
    private Retrofit mRetrofit;
    private String FILE_DIR = Environment.getExternalStorageDirectory() + File.separator + "downImage";
    private String FILE_NAME = "ok_" + System.currentTimeMillis() + ".jpg";

    public static RetrofitUtils getInstance(){
        if (mRetrofitUtils == null){
            synchronized (RetrofitUtils.class){
                if (mRetrofitUtils == null){
                    mRetrofitUtils = new RetrofitUtils();
                }
            }
        }
        return mRetrofitUtils;
    }

    public <T>T getRetrofitApi(Class<T> cls, String baseUrl){
        return getRetrofit(baseUrl).create(cls);
    }

    private Retrofit getRetrofit(String baseUrl){
        if (mRetrofit == null){
            mRetrofit = new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return mRetrofit;
    }

    public <T> void init(Observable<T>  observable, Subscriber<T>  subScribe){
        observable
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe(subScribe);
    }

    public <T> void init(Observable<T> observable, ApiCallback<T> callback){
        observable
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe(getSubscriber(callback));
    }

    private <T> Subscriber<T> getSubscriber(final ApiCallback<T> callback){
        return new Subscriber<T>() {
            @Override
            public void onCompleted() {
            }

            @Override
            public void onError(Throwable e) {
                callback.onError("调用失败");
                e.printStackTrace();
            }

            @Override
            public void onNext(T t) {
                callback.onSuccess(t);
            }
        };
    }
    
    public void writeFileToDisk(ResponseBody response, DownloadCallback callback) {
        InputStream is = null;
        FileOutputStream fos = null;
        try {
            byte[] buff = new byte[2048];
            is = response.byteStream();
            File filePath = new File(FILE_DIR);
            if (!filePath.exists()) {
                filePath.mkdirs();
            }
            final File file = new File(filePath, FILE_NAME);
            fos = new FileOutputStream(file);

            int len = 0;
            while ((len = is.read(buff)) != -1) {
                fos.write(buff, 0, len);
            }
            callback.getImageFile(file.getAbsolutePath());
            fos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public interface ApiCallback<T>{
        void onSuccess(T t);
        void onError(String msg);
    }

    public interface DownloadCallback{
        void getImageFile(String imgFile);
    }
}
  • GETQueryMap的使用
                Map<String, String> options = new HashMap<>();
                options.put("q", "小王子");
                options.put("tag", "");
                options.put("start", "0");
                options.put("count", "3");
                mRetrofitUtils.init(mRetrofitUtils.getRetrofitApi(RetrofitApi.class, UrlUtils.RETROFIT_BASE_URL).getDuBanDataUseQueryMap1(options), new Subscriber<BookInfo>() {
                    @Override
                    public void onCompleted() {
                        ToastUtils.showShort("调用成功");
                    }

                    @Override
                    public void onError(Throwable e) {
                        ToastUtils.showShort("调用失败");
                        e.printStackTrace();
                    }

                    @Override
                    public void onNext(BookInfo bookInfo) {
                        List<Book> results = bookInfo.getBooks();
                        for (Book book : results) {
                            Log.e("LHC", "bookTitle:" + book.getTitle() + ", bookAuthor:" + book.getAuthor()[0]);
                        }
                    }
                });
  • POSTFieldMap的使用
                Map<String, String> map = new HashMap<>();
                map.put("app", "weather.future");
                map.put("weaid", "1");
                map.put("appkey", "10003");
                map.put("sign", "b59bc3ef6191eb9f747dd4e83c99f2a4");
                map.put("format", "json");
                mRetrofitUtils.init(mRetrofitUtils.getRetrofitApi(RetrofitApi.class, UrlUtils.POS_HTTP_WEATHER).getWeathersData1(map), new RetrofitUtils.ApiCallback<Weathers>() {
                    @Override
                    public void onSuccess(Weathers weathers) {
                        List<WeatherInfo> results = weathers.getResult();
                        for (WeatherInfo weatherInfo : results) {
                            Log.e("LHC", "cityName:" + weatherInfo.getCitynm() + ", days:" + weatherInfo.getDays() + ", temperature:" + weatherInfo.getTemperature());
                        }
                    }

                    @Override
                    public void onError(String msg) {
                        ToastUtils.showShort(msg);
                    }
                });
  • 下载文件
                mRetrofitUtils.init(mRetrofitUtils.getRetrofitApi(RetrofitApi.class, UrlUtils.URL_IMAGE_01).downFile1(), new RetrofitUtils.ApiCallback<ResponseBody>() {
                    @Override
                    public void onSuccess(final ResponseBody responseBody) {
                        ThreadUtil.getInstance().getExecutorService().execute(new Runnable() {
                            @Override
                            public void run() {
                                mRetrofitUtils.writeFileToDisk(responseBody, new RetrofitUtils.DownloadCallback() {
                                    @Override
                                    public void getImageFile(final String imgFile) {

                                        runOnUiThread(new Runnable() {
                                            @Override
                                            public void run() {
                                                Glide.with(RetrofitRxJavaFZActivity.this).load(imgFile).into(ivShow);
                                            }
                                        });
                                    }
                                });
                            }
                        });
                    }

                    @Override
                    public void onError(String msg) {
                        ToastUtils.showShort(msg);
                    }
                });
  • 上传文件
    @Multipart
    @POST("xxx/uploadImage")
    Observable<FileBean> upLoadMultiFile2(@Part MultipartBody.Part[] parts);

                File file = new File(Environment.getExternalStorageDirectory()+File.separator+"crop_image.jpg");
                File file1 = new File(Environment.getExternalStorageDirectory()+File.separator+"temp.png");
                List<File> files = new ArrayList<>();
                files.add(file);
                files.add(file1);
                MultipartBody.Part[] parts = new MultipartBody.Part[files.size()];
                for (int i = 0; i < files.size(); i++) {
                    File fileItem = files.get(i);
                    RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), fileItem);
                    MultipartBody.Part filePart = MultipartBody.Part.createFormData("files", fileItem.getName(), requestFile);
                    parts[i] = filePart;
                }
                mRetrofitUtils.init(mRetrofitUtils.getRetrofitApi(RetrofitApi.class, UrlUtils.URL_UPLOAD_BASE).upLoadMultiFile2(parts), new RetrofitUtils.ApiCallback<FileBean>() {
                    @Override
                    public void onSuccess(FileBean bean) {
                        Log.e("LHC", "imgUrl:\n"+ bean.getResult().get(0)+"\n"+bean.getResult().get(1));
                    }

                    @Override
                    public void onError(String msg) {
                        ToastUtils.showShort("上传失败");
                    }
                });

这样Retrofit基本使用就完成了。
OKGO是对Retrofit的再次封装,使用很方便,地址:https://github.com/jeasonlzy/okhttp-OkGo

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容