Retrofit源码解析

如果你还没了解Retrofit如何使用,可以先查看这篇文章:Retrofit使用指南

一般分析源码都习惯从使用开始分析,那么接下来先从实例化Retrofit开始分析。

new Retrofit.Builder()
            .baseUrl("https://api.github.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build();

此处使用了builder设计模式实例化Retrofit,不管build()方法里面执行了什么,但最终还是需要调用Retrofit的构造函数的,那么我们看它的构造函数:

 Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
     List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,
     Executor callbackExecutor, boolean validateEagerly) {
   this.callFactory = callFactory;
   this.baseUrl = baseUrl;
   this.converterFactories = unmodifiableList(converterFactories); // Defensive copy at call site.
   this.adapterFactories = unmodifiableList(adapterFactories); // Defensive copy at call site.
   this.callbackExecutor = callbackExecutor;
   this.validateEagerly = validateEagerly;
 }

先不关心这些参数到底有什么作用,再回顾一下我们是如何使用Retrofit请求接口的:

创建一个接口,通过申明方法来请求接口,

public interface TestService {    
    //定义一条接口请求
    @GET("users/yuhengye")
    Call<GitHubUser> getUserInfo();
}

调用请求接口:

TestService testService = getRetrofit().create(TestService.class);
Call<GitHubUser> call = testService.getUserInfo();
call.enqueue(new Callback<String>() {
    @Override
    public void onResponse(Call<GitHubUser> call, Response<GitHubUser> response) {}

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

上面代码里是直接调用我们申明的接口方法testService.getUserInfo(),但是接口申明的方法肯定都是未实现的,可以直接调用的话证明我们一定是实现了这个接口,然而我们根本没有实现过TestService这个接口,我们是通过Retrofitcreate(TestService.class)方法得到实现列TestService接口的实例,那么看一下create()方法:

public <T> T create(final Class<T> service) {
  //验证service是否是接口类型,并且没有继承任何接口
  Utils.validateServiceInterface(service);
  if (validateEagerly) {
    //提前加载TestService申明的方法
    eagerlyValidateMethods(service);
  }
  //通过动态代理创建TestService接口的实例
  return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
      new InvocationHandler() {
      //具体代码实现下文再分析
      });
}

如果想了解更多Java的动态代理知识的话可以去参考其他资料了解下,这里简单说明下调用过程,上面代码最后实例化了一个TestService的动态代理,可以理解为这个动态代理实现了TestService接口,每次调用接口里的方法并且返回值时,都会通过调用创建的InvocationHandlerinvoke()方法返回值,也就是说当我们调用TestServicegetUserInfo()得到的值是上面代码invoke()方法里return的值,那分析一下invoke()里发生了什么:

 new InvocationHandler() {
   private final Platform platform = Platform.get();

   @Override public Object invoke(Object proxy, Method method, Object... args)
       throws Throwable {
     // If the method is a method from Object then defer to normal invocation.
     // 如果申明该方法的类是Object, 则直接调用该方法
     if (method.getDeclaringClass() == Object.class) {
       return method.invoke(this, args);
     }
     // 如果方法是平台默认的方法,转移给具体平台实现类调用,Android的平台实现类没有默认的方法;
     if (platform.isDefaultMethod(method)) {
       return platform.invokeDefaultMethod(method, service, proxy, args);
     }
     // 加载接口的方法
     ServiceMethod serviceMethod = loadServiceMethod(method);
     OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
     // 通过CallAdapter的adapt()方法转换得到接口申明方法的值
     return serviceMethod.callAdapter.adapt(okHttpCall);
   }
 }

首先开始调用了Platform.get()得到了当前使用的平台,实际是调用了这个方法:

 private static Platform findPlatform() {
   try {
     Class.forName("android.os.Build");
     if (Build.VERSION.SDK_INT != 0) {
       return new Android();
     }
   } catch (ClassNotFoundException ignored) {
   }
   try {
     Class.forName("java.util.Optional");
     return new Java8();
   } catch (ClassNotFoundException ignored) {
   }
   try {
     Class.forName("org.robovm.apple.foundation.NSObject");
     return new IOS();
   } catch (ClassNotFoundException ignored) {
   }
   return new Platform();
 }

这段代码不难理解,通过Class.forName()判断出当前所使用的平台,一共有AndroidJava8IOS这3个平台,再看看Android平台继承类的具体实现:

 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);
     }
   }
 }

只是重载类Platform的两个方法,不过可以看到里面申明了一个MainThreadExecutor类,里面有用到Android SDK的Handler实现在主线程执行Runnable,那么我们在什么时候需要用到这个东西呢,回想一下我们在使用Retrofit异步请求网络并且在回调里得到成功或是失败的结果,在这里肯定是需要在主线程执行回调的,不然使用起来还要自己切换主线程更新UI,那么到底是不是,我们下文通过源码验证。

回到上文提到的InvocationHandler,当执行到if(platform.isDefaultMethod(method))时,因为Platform默认都返回false,只有Java8才会走这一步,此处略过,接下来到ServiceMethod serviceMethod = loadServiceMethod(method);

 ServiceMethod loadServiceMethod(Method method) {
   ServiceMethod result;
   synchronized (serviceMethodCache) {
   // 从Map缓存中获取方法对应的ServiceMethod,没有则实例化,并且放进缓存中
     result = serviceMethodCache.get(method);
     if (result == null) {
       result = new ServiceMethod.Builder(this, method).build();
       serviceMethodCache.put(method, result);
     }
   }
   return result;
 }

此方法重点是实例化ServiceMethod,主要用到的参数是Retrofit和接口方法Method,Map缓存中把Method作为Key,证明一个接口方法对应着一个ServiceMethod,并且也是用到了builder去实例化,我们看一下build()发生了什么:

public ServiceMethod build() {
  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?");
  }
  responseConverter = createResponseConverter();

  // 解析接口方法申明的注解
  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation);
  }

 /...此处是一些参数验证等,具体实现请查看源码.../

  return new ServiceMethod<>(this);
}

最终调用ServiceMethod的构造函数:

  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;
  }

以上变量大多从变量名字就可以猜测出其含义,当InvocationHandlerinvoke()方法通过loadServiceMethod(method)解析接口申明的方法后,最后会执行以下代码:

OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);

OkHttpCall的构造函数是这样的:

final class OkHttpCall<T> implements Call<T> {
        
    private final ServiceMethod<T> serviceMethod;
    private final Object[] args;            
        
    OkHttpCall(ServiceMethod<T> serviceMethod, Object[] args) {
        this.serviceMethod = serviceMethod;
        this.args = args;
    }
    /...其余代码略过.../
}

先实例化一个OkHttpCall,最后通过调用ServiceMethod的成员变量callAdapteradapt方法得到我们在接口申明方法里定义的返回值,而OkHttpCall继承了RetrofitCall接口:

public interface Call<T> extends Cloneable {
    //同步执行网络请求
    Response<T> execute() throws IOException;
    //异步执行网络请求
    void enqueue(Callback<T> callback);
    //是否已经执行过请求    
    boolean isExecuted();
    //取消请求    
    void cancel();
    //请求是否已经取消 
    boolean isCanceled();
    //复制一个请求 
    Call<T> clone();
    //返回okhttp3.Request 
    Request request();
}

因为Retrofit没有实现具体的网络请求,默认是使用OkHttp请求网络的,OkHttpCall内部是调用OkHttpCall去请求网络得到请求结果okhttp3.Response,再进一步包装成RetrofitResponse,如果是异步请求的话,再把okhttp3.Callback回调包装成RetrofitCallback

OkHttpCall<T>的构造函数可以看出它的泛型是由它构造函数里的参数ServiceMethod<T>决定的,而后者的泛型又和它的成员变量private final Converter<ResponseBody, T> responseConverter;所对应的,而这个成员变量的赋值是发生在上文所提到的ServiceMethod.Builderbuild()方法里responseConverter = createResponseConverter();
以下是createResponseConverter()函数的源码:

  private Converter<ResponseBody, T> createResponseConverter() {
     Annotation[] annotations = method.getAnnotations();
     try {
       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);
     }
}

retrofit.responseBodyConverter()最终又调用了RetrofitnextResponseBodyConverter

  public <T> Converter<ResponseBody, T> nextResponseBodyConverter(Converter.Factory skipPast,
      Type type, Annotation[] annotations) {
    checkNotNull(type, "type == null");
    checkNotNull(annotations, "annotations == null");

    int start = converterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = converterFactories.size(); i < count; i++) {
      Converter<ResponseBody, ?> converter =
          converterFactories.get(i).responseBodyConverter(type, annotations, this);
      if (converter != null) {
        //noinspection unchecked
        return (Converter<ResponseBody, T>) converter;
      }
    }

    /...部分代码略过.../
  }

以上代码就是遍历了一个converterFactories集合,如果符合条件就返回该Converter.Factory工厂生成的Converter<ResponseBody, T>,最后遍历完也找不到合适的转换器Converter就抛出异常,再看一下Converter是怎么定义的:

public interface Converter<F, T> {
  /**
    * 把对象F转换成T对象返回;
    */
  T convert(F value) throws IOException;

  /** Creates {@link Converter} instances based on a type and target usage. */
  abstract class Factory {
  
    /**
     * 返回一个retrofit2.Converter对象, 这个对象可以把HTTP的ResponseBody转换成type参数所对应的类型,
     * 如果该工厂不能处理则返回空。这个方法用于创建响应类型的转换器, 类似Call<SimpleResponse>这样的申明,
     * 负责把ResponseBody类型转换成SimpleResponse类型;
     */
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
        Retrofit retrofit) {
      return null;
    }

    /**
     * 返回一个retrofit2.Converter对象, 这个对象可以把type参数所对应的类型转换成HTTP的RequestBody,
     * 如果该工厂不能处理则返回空。这个方法用于创建可以把Body、Part、PartMap等注解对应的值转换成RequestBody类型的转换器
     */
    public Converter<?, RequestBody> requestBodyConverter(Type type,
        Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
      return null;
    }

    /**
     * 返回一个retrofit2.Converter对象, 这个对象可以把type参数所对应的类型转换成String,
     * 如果该工厂不能处理则返回空。这个方法用于创建可以把Field、FieldMap、Header、HeaderMap、Path、
     * Query、QueryMap等注解对应的值转换成String类型的转换器
     */
    public Converter<?, String> stringConverter(Type type, Annotation[] annotations,
        Retrofit retrofit) {
      return null;
    }
  }
}

那么这个ServiceMethod在得到匹配的responseConverter后肯定是在OkHttp请求成功后把它的结果ResponseBody交给转换器转换成相应的类型,怎么请求网络呢,看下OkHttpCallenqueue()execute()方法:

final class OkHttpCall<T> implements Call<T> {
        
    private okhttp3.Call rawCall;

      @Override public void enqueue(final Callback<T> callback) {
        if (callback == null) throw new NullPointerException("callback == null");
    
        okhttp3.Call call;
        
        /...略过.../
        call = rawCall = createRawCall();
        /...略过.../
        
        call.enqueue(new okhttp3.Callback() {/...略过.../});
      }            
        
    @Override public Response<T> execute() throws IOException {
        okhttp3.Call call;
    
        /...略过.../
        call = rawCall = createRawCall();
        /...略过.../
        
        //把Http的请求的结果转成对应类型
        return parseResponse(call.execute());
    }
    
    private okhttp3.Call createRawCall() throws IOException {
        //通过serviceMethod配合接口方法的参数args得到OkHttp的Request;
        Request request = serviceMethod.toRequest(args);
        //serviceMethod的callFactory是由retrofit提供的,默认是OkHttpClient
        //这个call工厂负责把Request生成一个OkHttp的Call,用于操控网络请求
        okhttp3.Call call = serviceMethod.callFactory.newCall(request);
        if (call == null) {
         throw new NullPointerException("Call.Factory returned null.");
        }
        return call;
    }
    
    Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
        ResponseBody rawBody = rawResponse.body();
        
        /...略过.../
        
        ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
        try {
         T body = serviceMethod.toResponse(catchingBody);
         return Response.success(body, rawResponse);
        } catch (RuntimeException e) {
         // If the underlying source threw an exception, propagate that rather than indicating it was
         // a runtime exception.
         catchingBody.throwIfCaught();
         throw e;
        }
    }  
          
    /...其余代码略过.../
}

先是执行createRawCall()生成OkhttpCall,在OkHttp的网络请求执行完后,通过parseResponse()方法转换成对应类型,里面实际是调用了serviceMethod.toResponse()方法进行转换的:

  /** Builds a method return value from an HTTP response body. */
  T toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
  }

正是在实例化ServiceMethod的时候得到合适的转换器,每次请求成功后调用转换器的convert()方法把ResponseBody转换成对应的类型;

既然我们可以正常使用接口,那么肯定是找到了合适的转换器Converter,而转换器又是由它的工厂集合遍历得到的,这个工厂集合converterFactoriesRetrofit的成员变量,在Retrofit.Builder类的构造函数初始化的:

public static final class Builder {

    private List<Converter.Factory> converterFactories = new ArrayList<>();

    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 Builder() {
        this(Platform.get());
    }
    
    /...其余代码略过.../
}

可以看到已经提供了一个默认的转换器工厂实现类BuiltInConverters:

final class BuiltInConverters extends Converter.Factory {
  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    if (type == ResponseBody.class) {
      if (Utils.isAnnotationPresent(annotations, Streaming.class)) {
        return StreamingResponseBodyConverter.INSTANCE;
      }
      return BufferingResponseBodyConverter.INSTANCE;
    }
    if (type == Void.class) {
      return VoidResponseBodyConverter.INSTANCE;
    }
    return null;
  }

  @Override
  public Converter<?, RequestBody> requestBodyConverter(Type type,
      Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    if (RequestBody.class.isAssignableFrom(Utils.getRawType(type))) {
      return RequestBodyConverter.INSTANCE;
    }
    return null;
  }

  @Override public Converter<?, String> stringConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    if (type == String.class) {
      return StringConverter.INSTANCE;
    }
    return null;
  }
}

BuiltInConvertersresponseBodyConverter方法只支持RequestBodyVoid的类型转换,也就是说,我们在申明接口方法时,默认是只支持Call<RequestBody>Call<Void>这样的返回值;可是我们上文申明的却是Call<GitHubUser>,这是因为上文我们在实例化Retrofit的时候通过addConverterFactory(GsonConverterFactory.create())方法给Retrofit的转换器工厂集合增加了一个Gson转换器工厂,这个工厂会根据接口申明方法里返回值的泛型对应的Type生成一个GsonResponseBodyConverter转换器,把RequestBody类型转换成了GitHubUser类型:

final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
  private final Gson gson;
  private final TypeAdapter<T> adapter;

  GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
    this.gson = gson;
    this.adapter = adapter;
  }

  @Override public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      return adapter.read(jsonReader);
    } finally {
      value.close();
    }
  }
}

那么在上文创建的InvocationHandler类里的invoke()方法里所得到的OkHttpCall<T>实际上已经符合我们在接口方法里申明的返回值,像OkHttpCall<GitHubUser>这样,为什么在invoke()最后不直接返回OkHttpCall<GitHubUser>,而是调用return serviceMethod.callAdapter.adapt(okHttpCall);呢?先看一下adapt()方法的申明:

public interface CallAdapter<T> {

    //将一个Call<R>实例转换成T;
    <R> T adapt(Call<R> call);
    
    /...其余代码略过.../
}

这个方法跟上文里的提到的转换器Converter很相似,不过这里是限定了必须把Call<R>转换成其他类型返回;

在Android里必须在主线程更新UI,而OkHttpCall执行回调的时候没有进行线程切换,不同平台的默认实现也不一样,在Android平台里默认的CallAdapterFactory生成的CallAdapteradapt(okHttpCall)方法返回了一个ExecutorCallbackCall,也是继承了RetrofitCall接口:

static final class ExecutorCallbackCall<T> implements Call<T> {
    final Executor callbackExecutor;
    final Call<T> delegate;

    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
    }
    
    /...其余代码略过.../
}

实际上在实现Call接口的方法时,都是用成员变量delegate,也就是okHttpCall去实现,唯一不同的是在执行异步请求enqueue()方法时,在执行回调是是通过callbackExecutor去执行,而在Android平台里的默认的callbackExecutor对应是上文提到的MainThreadExecutor,通过Handler在主线程执行回调,也可以在实例化Retrofit的时候使用它的BuildercallbackExecutor(executor)方法使用自定义的Executor;然而callAdapter.adapt(okHttpCall)的作用还不仅仅是为了方便在主线程执行异步请求的回调,因为我们可以在实例化Retrofit的时候调用addCallAdapterFactory()方法增加自己的CallAdapter工厂,把Call<R>类型转换成你想申明的任何类型,比较典型的应用场景是结合RxJava,这样我们就可以在接口申明方法时这样申明:

public interface TestService {
    @GET("users/yuhengye")
    Observable<GitHubUser> getUserInfo();
}

然后这样请求网络:

RetrofitManager
            .getTestService()
            .getUserInfo()//返回一个Observable<GitHubUser>对象
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Subscriber<GitHubUser>() {
                @Override
                public void onCompleted() {
                }
                @Override
                public void onError(Throwable e) {
                }
                @Override
                public void onNext(GitHubUser result) {
                }
            });

通过返回Observable<GitHubUser>对象请求网络后需要自己操纵线程的切换执行回调,对比一下文章开始用Call<GitHubUser>返回值去请求网络并没有什么优势,但是用RxJava可以很优雅、方便地实现合并多个网络请求最后统一执行回调、一条网络请求完成后紧接着执行下一条网络请求等需求,如果用最原始的Call<GitHubUser>去实现这些需求则要自己实现,并且可能会引起多重嵌套,有关Retrofit配合RxJava的使用后面会另外再写一篇文章;

上文已经分析了Retrofit的实现原理,现在我们再总结一下Retrofit的构造函数参数所代表的含义:

callFactory: 该工厂负责生成一个OkHttpCall,用于请求网络,一般是使用OkHttpClient;

baseUrl: 接口前缀地址

converterFactories: 转换工厂集合,负责把HTTP请求的参数(传给接口方法的参数)转换成RequestBody
或者String,以及把HTTP请求成功后的结果ResponseBody
转换成接口方法申明的对应泛型类型;

adapterFactories:适配器工厂集合,负责把HTTP请求成功后的Call(实际上就是OkHttpCall)转换成自定义的类型;

callbackExecutor:当接口方法申明的返回值类型是Call<T>这种类型时,由默认提供的平台适配器转换,并且在执行异步请求回调时使用callbackExecutor去执行回调,主要是为了方便线程切换;

validateEagerly:如果该值为true,在使用Retrofitcreate()方法创建接口的实例时,会生成所有的ServiceMethod,如果为false,则在该接口方法第一次请求时才去创建;

以上就是Retrofit的主要实现源码分析;
最后总结一下在Retrofit里调用我们申明接口方法的主要流程:

检查是否有该接口方法对应的ServiceMethod缓存,没有则生成一个加入缓存,并且解析接口方法的注解,以及方法参数的注解,生成对应的参数处理器用于处理我们传给方法的参数;以及创建合适的responseConverter,它可以把网络请求结果ResponseBody转换成该接口方法返回值所需要的类型,这个接口方法返回值所需要的类型又是由CallAdapter提供的;默认的CallAdapter是这样的处理的:如果接口方法返回类型是Call<String>,则这里所需要的类型是String;这样在调用接口方法的时候,实际是先生成一个OkHttpCall<String>,这个对象再经由合适的CallAdapteradapt()方法转换成在接口方法里所申明的返回值类型;

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

推荐阅读更多精彩内容