Retrofit 源码分析

最近非常流行 Retrofit+RxJava+OkHttp 这一整套的网络请求和异步操作的开源框架,从 Jake Wharton 的 Github 主页也能看出其趋势。

Retrofit+RxJava+OkHttp.png

本文主要介绍 Retrofit 的基本原理,基于 2.1.0 版本的源码。

1. 基本用法

1.1 定义 API 接口

Retrofit 的使用非常简单,先来看一个 官网 上的示例代码。

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

官方的解释是

Retrofit turns your HTTP API into a Java interface.

首先定义了一个 API 接口 GitHubService,包含 HTTP 请求的方法 GET 和参数 user,及成功后的返回类型 List<Repo>,方法和参数由注解声明,非常清晰。

1.2 创建 Retrofit 对象并生成 API 实例

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

然后创建一个 Retrofit 对象,这里采用 Builder 模式,传入 baseUrlConverterFactory 等参数,后面会讲到。

通过 Retrofit 对象用动态代理的方式生成我们需要的 API 实例 GitHubService

1.3 API 实例去请求服务

Call<List<Repo>> repoCall = service.listRepos("danke77");

用生成的 API 实例调用相应的方法,默认返回 Call<T>,然后调用 Call#execute 方法同步或调用 Call#enqueue 方法异步请求 HTTP。

List<Repo> repos = repoCall.execute().body();

请求返回的数据直接转化成了 List<Repo>,非常方便。

如果要使用 RxJava,在创建 Retrofit 对象时要调用 addCallAdapterFactory(RxJavaCallAdapterFactory.create()),则返回类型会从 Call<T> 转换成 Observable<T>

2. Retrofit

2.1 build

先看一下 Retrofit.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;

Platform 提供了3个平台:Java8AndroidIOS,全都继承自 Platform,初始化时静态方法 Platform#findPlatform 会自动识别属于哪一个。

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

通过 Retrofit.Builder#clientRetrofit.Builder#callFactory 可以自定义 OkHttpClient

public Builder client(OkHttpClient client) {
    return callFactory(checkNotNull(client, "client == null"));
}

public Builder callFactory(okhttp3.Call.Factory factory) {
    this.callFactory = checkNotNull(factory, "factory == null");
    return this;
}

如果不指定 callFactory,则默认使用 OkHttpClient

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

converterFactoriesadapterFactories 提供了2个工厂列表,用于用户自定义数据转换和类型转换,后面会详细说明。

最后调用 Retrofit.Builder#build 创建一个 Retrofit 对象。

public Retrofit build() {
    // ... configured values
    
    return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
}

2.2 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() {
          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.
              if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
              }
              if (platform.isDefaultMethod(method)) {
                  return platform.invokeDefaultMethod(method, service, proxy, args);
              }
              ServiceMethod serviceMethod = loadServiceMethod(method);
              OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
              return serviceMethod.callAdapter.adapt(okHttpCall);
          }
    });
}

首先调用 Utils.validateServiceInterface 去判断 service 是否是一个 Interface 且没有继承其他 Interface,否则抛非法参数异常。

static <T> void validateServiceInterface(Class<T> service) {
    if (!service.isInterface()) {
        throw new IllegalArgumentException("API declarations must be interfaces.");
    }
    if (service.getInterfaces().length > 0) {
        throw new IllegalArgumentException("API interfaces must not extend other interfaces.");
    }
}

如果 validateEagerlytrue,则会调用 eagerlyValidateMethods 方法,会去预加载 service 中的所有方法,默认为 false

private void eagerlyValidateMethods(Class<?> service) {
    Platform platform = Platform.get();
    for (Method method : service.getDeclaredMethods()) {
        if (!platform.isDefaultMethod(method)) {
          loadServiceMethod(method);
        }
    }
}

然后就是通过动态代理生成 service。前两个 if 分支分别判断是否是 Object 的方法及 default 方法,后者除了 Java8 其他都是 false

再看 loadServiceMethod

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

通过 serviceMethodCache 实现缓存机制,同一个 service 的同一个方法只会创建一次,生成 ServiceMethod 同时存入 Cache

ServiceMethod 和需要的参数生成一个 OkHttpCall 对象。然后用 CallAdapter 将生成的 OkHttpCall 转换为我们需要的返回类型,这个后面会说到。

3. ServiceMethod

Adapts an invocation of an interface method into an HTTP call.

ServiceMethod 的作用就是把一个 API 方法转换为一个 HTTP 调用。

Retrofit#loadServiceMethod 方法中可以看出一个 API 方法对应一个 ServiceMethod

GitHubService为例

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

@GET("users/{user}/repos")MethodAnnotations@Path("user") String userParameterAnnotationsCall<List<Repo>>CallAdapter

3.1 ServiceMethod.Builder

先看下 ServiceMethod.Builder 的构造函数

public Builder(Retrofit retrofit, Method method) {
    this.retrofit = retrofit;
    this.method = method;
    this.methodAnnotations = method.getAnnotations();
    this.parameterTypes = method.getGenericParameterTypes();
    this.parameterAnnotationsArray = method.getParameterAnnotations(); 
}

传入 retrofitmethod 对象,通过 method 获取到方法注解 methodAnnotations、参数类型 parameterTypes 和参数注解 parameterAnnotationsArrayparameterAnnotationsArray是一个二维数组 Annotation[][],因为一个参数可以有多个注解。

关键看 ServiceMethod.Builder#build 方法,会生成一个 ServiceMethod 对象,接下来按顺序解析。

3.2 createCallAdapter

callAdapter = createCallAdapter() 会遍历 adapterFactories,通过 API 方法的 annotationsreturnType 取到第一个符合条件的 CallAdapter

for (int i = start, count = adapterFactories.size(); i < count; i++) {
    CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
    if (adapter != null) {
        return adapter;
    }
}

然后取到 responseType = callAdapter.responseType() 并进行判断,如果是 retrofit2.Response<T>okhttp3.Response 则抛异常,目前支持的有默认的 retrofit2.Call<T>,RxJava 的 rx.Observable,Java8 的 java.util.concurrent.CompletableFuture 和 Guava 的 com.google.common.util.concurrent.ListenableFuture

3.3 createResponseConverter

responseConverter = createResponseConverter() 会遍历 converterFactories,通过 API 方法的 annotationsresponseType 取到第一个符合条件的 Converter<ResponseBody, T>

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

3.4 parseMethodAnnotation

for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation);
}

遍历 API 方法的所有 annotations,如实例中的 @GET("users/{user}/repos"),根据所属类型和值解析成 HTTP 请求需要的数据。

在 package retrofit2.http 下包含了所有的 Method AnnotationParameter Anootation

DELETEGETHEADPATHCPOSTPUTOPTIONSHTTP 类型的 annotaion 会调用 parseHttpMethodAndPath,生成 httpMethodhasBodyrelativeUrlrelativeUrlParamNames

retrofit2.http.Headers 类型的 annotaion 会调用 parseHeaders,生成 headers

MultipartFormUrlEncoded 类型的 annotaion 分别生成 isMultipartisFormEncoded,两者互斥,不能同时为 true,否则会抛异常。

然后对生成的部分参数检查,httpMethod 不能为空,如果 hasBodyfalse,则 isMultipartisFormEncoded 必须也为 false,否则会抛异常。

if (httpMethod == null) {
    throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}

if (!hasBody) {
    if (isMultipart) {
        throw methodError(
            "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
    }
    if (isFormEncoded) {
        throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
            + "request body (e.g., @POST).");
    }
}

3.5 parseParameterAnnotation

int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
    ...  
    Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
    parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}

遍历每个参数的 parameterAnnotations,如实例中的 @Path("user") String user,根据所属类型和值解析成对应的 ParameterHandler,每个 Parameter Anootation 类型都有对应的 ParameterHandler,且每个参数只能有一个 ParameterHandler

可以看下 parseParameter 的逻辑

private ParameterHandler<?> parseParameter(
        int p, Type parameterType, Annotation[] annotations) {
    ParameterHandler<?> result = null;
    for (Annotation annotation : annotations) {
        ParameterHandler<?> annotationAction = parseParameterAnnotation(
            p, parameterType, annotations, annotation);

        if (annotationAction == null) {
            continue;
        }

        if (result != null) {
            throw parameterError(p, "Multiple Retrofit annotations found, only one allowed.");
        }

        result = annotationAction;
    }

    if (result == null) {
        throw parameterError(p, "No Retrofit annotation found.");
    }

    return result;
}

关键在 parseParameterAnnotation,根据 annotation 的类型并做参数校验,会生成不同的 ParameterHandler,如 RelativeUrl

if (type == HttpUrl.class
    || type == String.class
    || type == URI.class
    || (type instanceof Class && "android.net.Uri".equals(((Class<?>) type).getName()))) {
    return new ParameterHandler.RelativeUrl();
} else {
    throw parameterError(p, "@Url must be okhttp3.HttpUrl, String, java.net.URI, or android.net.Uri type.");
}

由于 Retrofit 不依赖 Android SDK,判断 type 时无法获取到 android.net.Uri.class,因此采用了 "android.net.Uri".equals(((Class<?>) type).getName()) 的技巧。

每个 Parameter Anootation 都会对应一个 ParameterHandler,如 static final class Path<T> extends ParameterHandler<T>,它们都实现了 ParameterHandler<T>

ParameterAnnotation ? extends ParameterHandler
Url RelativeUrl
Path Path
Query Query
QueryMap QueryMap
Header Header
HeaderMap HeaderMap
Field Field
FieldMap FieldMap
Part Part
PartMap PartMap
Body Body

每种 ParameterHandler 都通过 Converter<F, T> 将我们的传参类型转化成 RequestBuilder 需要的类型,并设置其参数。举个例子

static final class Query<T> extends ParameterHandler<T> {
    private final String name;
    private final Converter<T, String> valueConverter;
    private final boolean encoded;

    Query(String name, Converter<T, String> valueConverter, boolean encoded) {
        this.name = checkNotNull(name, "name == null");
        this.valueConverter = valueConverter;
        this.encoded = encoded;
    }

    @Override void apply(RequestBuilder builder, T value) throws IOException {
        if (value == null) return; // Skip null values.
        builder.addQueryParam(name, valueConverter.convert(value), encoded);
    }
}

valueConverter.convert 将我们的传参类型 T 转换成 String,并设置到 RequestBuilder 中。其他的配置也是按同样的方式。

4. OkHttpCall

OkHttpCall 实现了 retrofit2.Call<T>,看下它的构造函数,传入 ServiceMethod 和请求参数。

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

4.1 createRawCall

先看下 OkHttpCall#createRawCall

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

将已经生成的 serviceMethod 通过 toRequest(args) 转成一个 okhttp3.Request 对象。再用创建 Retrofit 时指定的 okhttp3.Call.Factory 创建一个 okhttp3.Call,这里如果不指定 okhttp3.Call.Factory,则默认是 okhttp3.OkHttpClient

ServiceMethod#toRequest 方法中,用 method 相关的配置生成一个 retrofit2.RequestBuilder 后,再用之前准备好的 parameterHandlers 处理每一个参数,最后生成一个 okhttp3.Request

/** Builds an HTTP request from method arguments. */
Request toRequest(Object... args) throws IOException {
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers, contentType, hasBody, isFormEncoded, isMultipart);

    @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

    // ... 校验
    
    for (int p = 0; p < argumentCount; p++) {
        handlers[p].apply(requestBuilder, args[p]);
    }

    return requestBuilder.build();
}

4.2 execute

okhttp3.Call#execute 用于同步请求 HTTP,线程会被阻塞,请求成功后返回我们指定的数据类型。

@Override 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 {
                call = rawCall = createRawCall();
            } catch (IOException | RuntimeException e) {
                creationFailure = e;
                throw e;
            }
        }
    }

    if (canceled) {
        call.cancel();
    }

    return parseResponse(call.execute());
}

首先检查 okhttp3.Call 是否已被执行。

一个 okhttp3.Call 只能被执行一次,可以调用 OkHttpCall#clone 重新创建一个新的相同配置的 HTTP 请求。

@Override public OkHttpCall<T> clone() {
    return new OkHttpCall<>(serviceMethod, args);
}

然后检查并创建 okhttp3.Call,调用 okhttp3.Call#execute 执行同步请求。

最后调用 parsePesponse 将返回的 okhttp3.Response 解析成我们需要的数据。

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();

    // Remove the body's source (the only stateful object) so we can pass the response along.
    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();

    int code = rawResponse.code();
    // ... 状态码检查

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

okhttp3.Response 进行一些状态码检查后调用 ServiceMethod#toResponse 生成我们需要的数据类型。这里就用到了我们之前准备好的 responseConverter。最后封装成一个 retrofit2.Response<T>,包含了原始的 rawResponse、我们需要的 bodyerrorBody,我们取需要的数据。

4.3 enqueue

okhttp3.Call#enqueueokhttp3.Call#execute 流程类似,异步请求 HTTP,然后将回调都交给 retrofit2.Callback<T> 处理。

看下 retrofit2.Callback#onFailure 的注释

Invoked when a network exception occurred talking to the server or when an unexpected exception occurred creating the request or processing the response.

这里 retrofit2.Callback#onFailure 除了处理网络异常外,还会处理创建网络请求和解析数据的异常,在回调中处理,而不是直接 crash,这点做的非常好。

  1. createRawCall 抛出的异常

    try {
        call = rawCall = createRawCall();
    } catch (Throwable t) {
        failure = creationFailure = t;
    }
    

    如果出异常,直接回调,不执行接下来 enqueue 方法。

  2. 执行 okhttp3.Call#enqueueokhttp3.Callback 抛出的网络请求异常

  3. 网络请求成功后在 okhttp3.Callback#onResponseparseResponse 时抛出的异常

    try {
        response = parseResponse(rawResponse);
    } catch (Throwable e) {
        callFailure(e);
        return;
    }
    

5. CallAdapter

前面已经简单介绍过 ServiceMethod#createCallAdapter,它会从 adapterFactories 中找到第一个符合条件的 CallAdapter.Factory

5.1 retrofit-adapters

先来看 Retrofit 提供的 retrofit-adapters 模块,目前供我们选择使用的有 guavajava8rxjava,分别对应的 CallAdapter.FactoryGuavaCallAdapterFactoryJava8CallAdapterFactoryRxJavaCallAdapterFactory

Retrofit#build 时,除了我们自己添加的 CallAdapter.Factory, 还会添加两个默认的 CallAdapter.FactoryExecutorCallAdapterFactoryDefaultCallAdapterFactory

// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

Retrofit 源码中有大量类似 List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); 的例子,不直接使用成员变量,而是将成员变量重新拷贝给一个新的临时变量,这样虽然多申请了4个字节内存,但如果以后将成员变量改成入参,就可以不用改代码直接使用了,是一种好的编码习惯。

5.2 default

再看默认的 CallAdapter.Factory

CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
    if (callbackExecutor != null) {
        return new ExecutorCallAdapterFactory(callbackExecutor);
    }
    return DefaultCallAdapterFactory.INSTANCE;
}

DefaultCallAdapterFactoryExecutorCallAdapterFactory 返回的类型都是 retrofit2.Call<R>

DefaultCallAdapterFactory 中,CallAdapter#adapt 什么都不做,直接返回 retrofit2.Call<R>。而 ExecutorCallAdapterFactoryCallAdapter#adapt 则返回 ExecutorCallbackCall<T>,它实现了 retrofit2.Call<R>,会传入一个 Executor,同步调用不变,异步调用时会在指定的 Executor 上执行。

5.3 adapt

看下 CallAdapteradapt 方法

/**
  * Returns an instance of {@code T} which delegates to {@code call}.
  */
<R> T adapt(Call<R> call);

它的作用就是把 retrofit2.Call<R> 转换成我们需要的 T

CallAdapters ? extends CallAdapter.Factory T
guava retrofit2.adapter.guava.GuavaCallAdapterFactory com.google.common.util.concurrent.ListenableFuture
java8 retrofit2.adapter.java8.Java8CallAdapterFactory java.util.concurrent.CompletableFuture
rxjava retrofit2.adapter.rxjava.RxJavaCallAdapterFactory rx.Observable
default retrofit2.ExecutorCallAdapterFactory retrofit2.Call<R>
default retrofit2.DefaultCallAdapterFactory retrofit2.Call<R>

List<CallAdapter.Factory> 中添加的顺序是我们指定的一个或多个 CallAdapter.Factory,默认的 ExecutorCallAdapterFactoryDefaultCallAdapterFactory,查找时按顺序查找。

6. Converter

Converter 的作用就是将 HTTP 请求返回的数据格式转换成我们需要的对象,或将我们提供的对象转换成 HTTP 请求需要的数据格式。

public interface Converter<F, T> {
    T convert(F value) throws IOException;
}

接口非常清晰,将 F 转换成 T

6.1 Factory

来看 Converter.Factory

abstract class Factory {
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        return null;
    }

    public Converter<?, RequestBody> requestBodyConverter(Type type,
        Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        return null;
    }

    public Converter<?, String> stringConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        return null;
    }
}
  1. responseBodyConverter 用于将 HTTP 请求返回的 response 转换成我们指定的类型。
  2. requestBodyConverter 用于将 BodyPartPartMap 3种类型的参数转换成 HTTP 的请求体。
  3. stringConverter 用于将 FieldFieldMapHeaderPathQueryQueryMap 这几种类型的参数转换成 String

6.2 retrofit-converters

CallAdapter 类似,Retrofit 也提供了默认的 Converter.Factory——BuiltInConverters,只能处理基本的 ResponseBodyRequestBodyString 类型。

在 Retrofit 提供的 retrofit-converters 模块,供我们选择的有 GsonJacksonMoshiProtocol BuffersXMLScalarWire,我们常用的有 GsonConverterFactory

需要注意的是,Jake Wharton 在他的一篇演讲 Simple HTTP with Retrofit 2 中说道

I want to stress that the order matters. This is the order in which we’re going to ask each one whether or not it can handle a type. What I have written above is actually wrong. If we ever specify a proto, it’s going to be encoded as JSON, which will try and deserialize the response buddy as JSON. That’s obviously not what we want. We will have to flip these because we want to check protocol buffers first, and then JSON through GSON.

说的就是 Retrofit.Builder#addConverterFactory 的顺序非常重要,先添加的 Converter.Factory 会先用来解析,而 Gson 非常强大,如果第一个添加 GsonConverterFactory,则其他想要转换的类型如 Protocol Buffers 就不会执行,因此建议将 GsonConverterFactory 作为最后一个添加

7. 总结

Retrofit 的源码还是很难的,反反复复看了很多遍,除了其原理和流程外,从中也学到了一些技巧和设计模式。在写这篇文章的同时又把思路和流程重新理了一遍,对自己帮助还是非常大的。关于源码最大的感受就是各个类之间传对象调用感觉非常乱,也增加了理解的难度,如 RetrofitServiceMethod 之间就互相依赖。

本文是 慌不要慌 原创,发表于 https://danke77.github.io/,请阅读原文支持原创 https://danke77.github.io/2016/08/06/retrofit-source-analysis/,版权归作者所有,转载请注明出处。

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

推荐阅读更多精彩内容