Android网络之Retrofit2.0使用和解析

Retrofit2在项目中的使用

Android studio项目添加依赖

javacompile 'com.squareup.retrofit2:retrofit:2.0.1'

项目中使用样例

定义HTTP API使用接口

javapublic interface GitHubService { 
      @GET("users/{user}/repos") 
      Call<List<Repo>> listRepos(@Path("user") String user);
}
  • 通过在接口上添加注解的方式来表示如何处理网络请求。
  • Retrofit支持5中类型的注解:GET,POST,PUT,DELETE和HEAD.
  • 可以使用不带参数的url @GET("users/list"),也可以使用带参数的url @GET("users/list?sort=desc")

构造Retrofit实例

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();
GitHubService service = retrofit.create(GitHubService.class);

创建同步或异步HTTP请求到远程网络服务器

Call<List<Repo>> repos = service.listRepos("octocat");

定制数据类型转换器

  • Gson: com.squareup.retrofit2:converter-gson
  • Jackson: com.squareup.retrofit2:converter-jackson
  • Moshi: com.squareup.retrofit2:converter-moshi
  • Protobuf: com.squareup.retrofit2:converter-protobuf
  • Wire: com.squareup.retrofit2:converter-wire
  • Simple XML: com.squareup.retrofit2:converter-simplexml
  • Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

GitHubService service = retrofit.create(GitHubService.class);

Retrofit使用扩展

自定义Gson类型转换器

/**
{
    "resultcode":0,
    "resultmsg":"请求成功",
    "result":{}
}
*/
public class Wrapper {
    public int resultcode ;
    public String resultmsg ;
    public Object result ;
    public static class JsonAdapter implements JsonDeserializer<Wrapper> {
        @Override
        public Wrapper deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            try {
                String jsonRoot = json.getAsJsonObject().toString() ;
                Wrapper wrapper = new Wrapper() ;
                JSONObject jsobRespData = new JSONObject(jsonRoot) ;
                wrapper.resultcode = jsobRespData.getInt("resultcode") ;
                wrapper.resultmsg = jsobRespData.getString("resultmsg") ;
                wrapper.result = jsobRespData.get("result") ;
                return wrapper;
            } catch (JSONException e) {
                throw new JsonParseException(e) ;
            }
        }
    }

}

添加到Retrofit当中

Gson gson = new GsonBuilder()
            .registerTypeAdapter(Wrapper.class, new Wrapper.JsonAdapter())
            .create() ;
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .addConverterFactory(GsonConverterFactory.create(gson))
    .build();

GitHubService service = retrofit.create(GitHubService.class);      

请求时添加head信息

在定义请求接口时添加:

@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();

@Headers({
    "Accept: application/vnd.github.v3.full+json",
    "User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);

@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
Retrofit依赖

如果所示在Retrofit2.0中只支持okhttp,所以另一种方法是在okhttp的拦截器中addheader。

Retrofit2源码解析

Retrofit请求框架实现了高度的解耦,通过解析注解的得到的代理类生成http请求,然后将请求交给OkHttp。通过在Retrofit创建时生成的Converter再将OkHttp返回的数据进行类型转换得到自己需要的数据。现在Rxjava响应式编程已经广泛应用,在使用Retrofit时也会结合RxJava使编码更加简单、高效。

一张图简单描述一下Retrofit的工作原理:


Retrofit工作原理

定义网络请求接口

public interface GitHubService {
     @GET("users/{user}/repos")
     Call<List<Repo>> listRepos(@Path("user") String user);
}

创建Retrofit实例

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    //支持RxJava
    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    .addConverterFactory(ByteArrayConverterFactory.create())
    .addConverterFactory(JSONObjectResponseConverterFactory.create())
    .addConverterFactory(StringResponseConverterFactory.create())
    //支持对象转json串
    .addConverterFactory(GsonConverterFactory.create())
    .build();
  • 设置BaseUrl
  • 添加CallAdapterFactory
  • 添加converterFactory
  • 此时也可以设置自定义的okHttpclient

接下来我们看

GitHubService service = retrofit.create(GitHubService.class);

Retrofit.create方法的详细介绍

public <T> T create(final Class<T> service) {
    //判断定义的接口服务是否可用
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          //判断Android,IOS,java8
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, Object... args)
              throws Throwable {
            //如果是对象里的方法直接调用
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            /**
            * 对java8做兼容,android和ios值恒为false
            */
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            //主要看这三行代码
            /**
            * 1、生成获取缓存中的method对应的ServiceMethod或者生产method对应的ServiceMethod
            * 2、生成OkHttpCall的实例
            * 3、根据生成的ServiceMethod实例中的callAdapter对象,调用callAdapter.adapt方法创建
            * 对应的Observable
            */
            ServiceMethod serviceMethod = loadServiceMethod(method);
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

Platfrom的获取

Platfrom
class Platform {
    //这个方法Android中为Plafrom默认的
    //Java8返回的是method.isDefault(),熟悉Java8的都知道这是Java8的新特性。。
    boolean isDefaultMethod(Method method) {
        return false;
    }
    //这个方法Android中有自己默认的实现MainThreadExecutor
    Executor defaultCallbackExecutor() {
        return null;
    }
    static class Android extends Platform {
        @Override 
        public Executor defaultCallbackExecutor() {
            return new MainThreadExecutor();
        }

        @Override 
        CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
            return new ExecutorCallAdapterFactory(callbackExecutor);
        }
        //Rx默认请求方式都是同步请求,所以我们在发出请求和请求结果回来的时候切换线程
        static class MainThreadExecutor implements Executor {
            private final Handler handler = new Handler(Looper.getMainLooper());
            @Override 
            public void execute(Runnable r) {
                handler.post(r);
            }
        }
    }
}

ServiceMethod对象的生成

先看一张我debug时候的ServiceMethod的图

ServiceMethod

ServiceMethod的获取

/**Retrofit.java
* 首先从serviceMethodCache缓存中获取,如果为null则创建
*/
ServiceMethod loadServiceMethod(Method method) {
    ServiceMethod result;
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = new ServiceMethod.Builder(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

ServiceMethod的创建

final class ServiceMethod<T> {
    //部分代码省略
    ServiceMethod(Builder<T> builder) {
        this.callFactory = builder.retrofit.callFactory();
        this.callAdapter = builder.callAdapter;
        this.baseUrl = builder.retrofit.baseUrl();
        this.responseConverter = builder.responseConverter;
        this.httpMethod = builder.httpMethod;
        this.relativeUrl = builder.relativeUrl;
        this.headers = builder.headers;
        this.contentType = builder.contentType;
        this.hasBody = builder.hasBody;
        this.isFormEncoded = builder.isFormEncoded;
        this.isMultipart = builder.isMultipart;
        this.parameterHandlers = builder.parameterHandlers;
    }
    static final class Builder<T> {
        public Builder(Retrofit retrofit, Method method) {
        this.retrofit = retrofit;
        this.method = method;
        this.methodAnnotations = method.getAnnotations();
        this.parameterTypes = method.getGenericParameterTypes();
        this.parameterAnnotationsArray = method.getParameterAnnotations();
    }

        public ServiceMethod build() {
            //创建CallAdapter<?>
            callAdapter = createCallAdapter();
            responseType = callAdapter.responseType();
            if (responseType == Response.class || responseType == okhttp3.Response.class) {
             throw methodError("'"
                 + Utils.getRawType(responseType).getName()
                 + "' is not a valid response body type. Did you mean ResponseBody?");
            }
            //创建Converter<ResponseBody, T>
            responseConverter = createResponseConverter();
            for (Annotation annotation : methodAnnotations) {
             parseMethodAnnotation(annotation);
            }
            /********/
            return new ServiceMethod<>(this);
        }
        
        private CallAdapter<?> createCallAdapter() {
            Type returnType = method.getGenericReturnType();
            /*******/
            Annotation[] annotations = method.getAnnotations();
            try {
                //调用retrofit中的方法进行创建
                return retrofit.callAdapter(returnType, annotations);
            } catch (RuntimeException e) { // Wide exception range because factories are user code.
                throw methodError(e, "Unable to create call adapter for %s", returnType);
            }
        }
        private Converter<ResponseBody, T> createResponseConverter() {
            Annotation[] annotations = method.getAnnotations();
            try {
                //调用retrofit中的方法进行创建
                return retrofit.responseBodyConverter(responseType, annotations);
            } catch (RuntimeException e) { // Wide exception range because factories are user code.
                throw methodError(e, "Unable to create converter for %s", responseType);
            }
        }
    }
}
CallAdapter的创建
public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
}

public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
  Annotation[] annotations) {
    checkNotNull(returnType, "returnType == null");
    checkNotNull(annotations, "annotations == null");
    int start = adapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
        CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
        if (adapter != null) {
            return adapter;
        }
    }
    /********************/
}

在创建Retrofit的时候我们添加过.addCallAdapterFactory(RxJavaCallAdapterFactory.create()),这是我们会调用RxJavaCallAdapterFactory.get 方法获取CallAdapter,通过源代码我们可以找到其返回的是new SimpleCallAdapter(observableType, scheduler)

Converter的创建
public <T> Converter<T, RequestBody> requestBodyConverter(Type type,
   Annotation[] parameterAnnotations, Annotation[] methodAnnotations) {
   return nextRequestBodyConverter(null, type, parameterAnnotations, methodAnnotations);
}

public <T> Converter<T, RequestBody> nextRequestBodyConverter(Converter.Factory skipPast,
    Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations) {
    checkNotNull(type, "type == null");
    checkNotNull(parameterAnnotations, "parameterAnnotations == null");
    checkNotNull(methodAnnotations, "methodAnnotations == null");
    int start = converterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = converterFactories.size(); i < count; i++) {
        Converter.Factory factory = converterFactories.get(i);
        Converter<?, RequestBody> converter =
            factory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, this);
        if (converter != null) {
            //noinspection unchecked
            return (Converter<T, RequestBody>) converter;
        }
    }
    /******/
}

相同的在创建Retrofit的时候我们也添加过许多的ConverterFactory,在寻找相匹配的Converter时我们是通过遍历在寻找到第一个合适的Converter返回。什么叫做合适的Converter,即该ConverterFactory能产生出匹配服务接口注解和返回类型。

retrofit的构造器中默认添加的适配器和转化器
public static final class Builder {
    private Platform platform;
    private okhttp3.Call.Factory callFactory;
    private HttpUrl baseUrl;
    private List<Converter.Factory> converterFactories = new ArrayList<>();
    private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
    private Executor callbackExecutor;
    private boolean validateEagerly;

    Builder(Platform platform) {
      this.platform = platform;
      // Add the built-in converter factory first. This prevents overriding its behavior but also
      // ensures correct behavior when using converters that consume all types.
      //自动回添加加默认的转化器
      converterFactories.add(new BuiltInConverters());
    }
    /****************/
    public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      //添加一个默认的适配器(Android、IOS、Java8)
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }

具体怎么从Factory中获取对应的Converter和Adapter我们从代码中可以直观的看到。

OkHttpCall的创建

OkHttpCall(ServiceMethod<T> serviceMethod, Object[] args) {
   this.serviceMethod = serviceMethod;
   this.args = args;
}

网络请求

请求的准备

serviceMethod.callAdapter.adapt(okHttpCall) 对应SimpleCallAdapter.adapt

static final class SimpleCallAdapter implements CallAdapter<Observable<?>> {
    private final Type responseType;
    private final Scheduler scheduler;
    SimpleCallAdapter(Type responseType, Scheduler scheduler) {
        this.responseType = responseType;
        this.scheduler = scheduler;
    }
    @Override public Type responseType() {
        return responseType;
    }
    @Override public <R> Observable<R> adapt(Call<R> call) {
        //创建请求的观察者,返回我们需要的Ovservable
        Observable<R> observable = Observable.create(new CallOnSubscribe<>(call)) //
          .lift(OperatorMapResponseToBodyOrError.<R>instance());
        if (scheduler != null) {
            return observable.subscribeOn(scheduler);
        }
        return observable;
    }
}
static class Android extends Platform {
    @Override
    public Executor defaultCallbackExecutor() {
        return new MainThreadExecutor();
    }
    @Override
    CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
        return new ExecutorCallAdapterFactory(callbackExecutor);
    }
    static class MainThreadExecutor implements Executor {
        private final Handler handler = new Handler(Looper.getMainLooper());
        @Override
        public void execute(Runnable r) {
            handler.post(r);
        }
    }
}
/*
* 因为默认的执行线程为主线程,所以我们要切换到subscribeOn(Schedulers.io())线程从而达到异步的目的。
* 然后通过observeOn(AndroidSchedulers.mainThread())将线程切回UI线程。
* 当Okhttp请求完数据并进行相应的convert之后,就可以在UI处理相应的逻辑。
*/
service.listRepos("octocat")
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(list->{ 
    if(list!=null){      
    //TODO 取得数据后逻辑处理     
    }     
});

请求的发起

回到CallAdapt方法,创建Observable,而new CallOnSubscribe<>(call)生成了一个OnSubscribe()的实例,而OnSubscribe继承自Action1,其只包含一个call()方法,而这个call是在CallOnSubscribe中实现:

static final class CallOnSubscribe<T> implements Observable.OnSubscribe<Response<T>> {
    private final Call<T> originalCall;
    CallOnSubscribe(Call<T> originalCall) {
    this.originalCall = originalCall;
    }
    @Override public void call(final Subscriber<? super Response<T>> subscriber) {
        // Since Call is a one-shot type, clone it for each new subscriber.
        Call<T> call = originalCall.clone();

        // Wrap the call in a helper which handles both unsubscription and backpressure.
        RequestArbiter<T> requestArbiter = new RequestArbiter<>(call, subscriber);
        subscriber.add(Subscriptions.create(requestArbiter));
        subscriber.setProducer(requestArbiter);
    }
}

首先clone了一份Call,然后生成了RequestArbiter,他继承自AtomicBoolean,实现了Subscription, Producer接口,Producer只有一个request方法;一般实现该接口的类,都会包含一个Subscriber对象和一个待处理的数据:

static final class RequestArbiter<T> extends AtomicBoolean implements Action0,
    Producer {
    private final Call<T> call;
    private final Subscriber<?super Response<T>> subscriber;
    RequestArbiter(Call<T> call, Subscriber<?super Response<T>> subscriber) {
        this.call = call;
        this.subscriber = subscriber;
    }
    @Override
    public void request(long n) {
        if (n < 0) {
            throw new IllegalArgumentException("n < 0: " + n);
        }
        if (n == 0) {
            return; // Nothing to do when requesting 0.
        }
        if (!compareAndSet(false, true)) {
            return; // Request was already triggered.
        }
        try {
            //进行网络请求
            Response<T> response = call.execute();
            if (!subscriber.isUnsubscribed()) {
                subscriber.onNext(response);
            }
        } catch (Throwable t) {
            Exceptions.throwIfFatal(t);
            if (!subscriber.isUnsubscribed()) {
                subscriber.onError(t);
            }
            return;
        }
        if (!subscriber.isUnsubscribed()) {
            subscriber.onCompleted();
        }
    }

    @Override
    public void call() {
        call.cancel();
    }
}

请求的执行

回顾创建Retrofit.create()代码中serviceMethod.callAdapter.adapt(okHttpCall),所以上面的call.execute() 就是OkHttpCall.execute

public Response<T> execute() throws IOException{
    okhttp3.Call call;
    synchronized (this) {
        if ( executed )
            throw new IllegalStateException( "Already executed." );
        executed = true;
        if ( creationFailure != null ){
            if ( creationFailure instanceof IOException ){
                throw (IOException) creationFailure;
            } else {
                throw (RuntimeException) creationFailure;
            }
        }
        call = rawCall;
        if ( call == null ){
            try {
                //获取okhttp实例
                call = rawCall = createRawCall();
            } catch ( IOException | RuntimeException e ) {
                creationFailure = e;
                throw e;
            }
        }
    }
    if ( canceled ){
        call.cancel();
    }
    //执行okhttp请求
    return(parseResponse( call.execute() ) );
}

private okhttp3.Call createRawCall() throws IOException{
    Request request = serviceMethod.toRequest( args );
    //serviceMethod构造中this.callFactory = builder.retrofit.callFactory();
    okhttp3.Call call = serviceMethod.callFactory.newCall( request );
    if ( call == null ){
        throw new NullPointerException( "Call.Factory returned null." );
    }
    return(call);
}

请求的OkHttpClient实例获取

public okhttp3.Call.Factory callFactory() {
    return callFactory;
}
public Builder callFactory(okhttp3.Call.Factory factory) {
    this.callFactory = checkNotNull(factory, "factory == null");
    return this;
}
//使用自定义OkHttpClient
public Builder client(OkHttpClient client) {
    return callFactory(checkNotNull(client, "client == null"));
}

public Retrofit build(){
    if ( baseUrl == null ){
        throw new IllegalStateException( "Base URL required." );
    }
    okhttp3.Call.Factory callFactory = this.callFactory;
    //没有自定义OkHttpClient,则会新创建一个
    if ( callFactory == null ){
        callFactory = new OkHttpClient();
    }
    Executor callbackExecutor = this.callbackExecutor;
    if ( callbackExecutor == null ){
        callbackExecutor = platform.defaultCallbackExecutor();
    }
    List<CallAdapter.Factory> adapterFactories = new ArrayList<>( this.adapterFactories );
    adapterFactories.add( platform.defaultCallAdapterFactory( callbackExecutor ) );
    List<Converter.Factory> converterFactories = new ArrayList<>( this.converterFactories );
    return(new Retrofit( callFactory, baseUrl, converterFactories, adapterFactories,
                 callbackExecutor, validateEagerly ) );
    }
}

请求结果的处理

Response<T> parseResponse( okhttp3.Response rawResponse ) throws IOException{
    ResponseBody rawBody = rawResponse.body();
    rawResponse = rawResponse.newBuilder()
              .body( new NoContentResponseBody( rawBody.contentType(), rawBody.contentLength() ) )
              .build();
    int code = rawResponse.code();
    if ( code < 200 || code >= 300 ){
        try {
            ResponseBody bufferedBody = Utils.buffer( rawBody );
            return(Response.error( bufferedBody, rawResponse ) );
        } finally {
            rawBody.close();
        }
    }
    if ( code == 204 || code == 205 ){
        return(Response.success( null, rawResponse ) );
    }
    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody( rawBody );
    try {
        //使用Converter将返回结果转化为接口返回的数据格式类型
        T body = serviceMethod.toResponse( catchingBody );
        //包装成Response并返回
        return(Response.success( body, rawResponse ) );
    } catch ( RuntimeException e ) {
        catchingBody.throwIfCaught();
        throw e;
    }
}

还记得创建Observable时 Observable<R> observable = Observable.create(new CallOnSubscribe<>(call)).lift(OperatorMapResponseToBodyOrError.<R>instance()); ,OperatorMapResponseToBodyOrError将包装的Response中的body取出来并进行发射。

总结

现在随着Rxjava响应式编程越来越多的程序猿使用,自己也开始接触和使用。Retrofit+Rxjava+okhttp是时下比较受欢迎的网络请求框架,其源代码并不是很多,其底层网络通信时交由 OkHttp来完成的,但是Retrofit运用了大量的设计模式,代码逻辑很清晰,笔者以前用的是AsyncHttpClient作为app的网络请求框架,其源码也没自己的研究过。但看完Retrofit代码之后觉得收获很大,建议如果感兴趣可以抽时间仔细的阅读。

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

推荐阅读更多精彩内容