Retrofit源碼解析

博文出處:Retrofit源碼解析饰抒,歡迎大家關(guān)注我的博客阔加,謝謝奢啥!

Header

之前對(duì) OkHttp 進(jìn)行過源碼分析了秸仙,那么今天就來講講 Retrofit 。

Retrofit 其實(shí)是對(duì) OkHttp 進(jìn)行了一層封裝桩盲,讓開發(fā)者對(duì)網(wǎng)絡(luò)操作更加方便快捷寂纪。

相信絕大多數(shù)的 Android 開發(fā)者都有使用過的經(jīng)歷。其 restful 風(fēng)格的編程俘獲了眾多人的心正驻。

廢話就不多講了弊攘,下面就要對(duì) Retrofit 進(jìn)行源碼解析抢腐。

本文解析的 Retrofit 基于 v2.3.0 ,GitHub 地址:https://github.com/square/retrofit

Retrofit 使用方法

直接抄官網(wǎng)的:

第一步襟交,聲明 API 接口:

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

第二步迈倍,構(gòu)造出 Retrofit 對(duì)象:

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

第三步,得到 API 接口捣域,直接調(diào)用:

GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = service.listRepos("octocat");

最后啼染,就是調(diào)用 repos 執(zhí)行 Call :

// sync
repos.execute();
// async
repos.enqueue(...);

請(qǐng)求源碼解析

我們先來看看發(fā)出網(wǎng)絡(luò)請(qǐng)求部分的源碼。

Retrofit.Builder

首先切入點(diǎn)就是 Retrofit.Builder 焕梅。

Retrofit.Builder 中有以下的方法:

  • client : 設(shè)置 http client迹鹅,默認(rèn)是 OkHttpClient,會(huì)調(diào)用 callFactory 方法
  • callFactory : 設(shè)置網(wǎng)絡(luò)請(qǐng)求 call 的工廠贞言,默認(rèn)就是上面的 OkHttpClient
  • baseUrl : api 的 base url
  • addConverterFactory : 添加數(shù)據(jù)轉(zhuǎn)換器工廠
  • addCallAdapterFactory⌒迸铩: 添加網(wǎng)絡(luò)請(qǐng)求適配器工廠
  • callbackExecutor : 回調(diào)方法執(zhí)行器
  • validateEagerly : 是否提前解析接口方法

這些都是用來配置 Builder 的。

那么我們來看下 Builder 的構(gòu)造方法:

public Builder() {
  // 確定平臺(tái)该窗,有 Android Java8 默認(rèn)Platform 三種
  this(Platform.get());
}

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.
  // 默認(rèn)內(nèi)置的數(shù)據(jù)轉(zhuǎn)換器 BuiltInConverters
  converterFactories.add(new BuiltInConverters());
}

來個(gè)小插曲弟蚀,我們來看下 Retrofit 是如何確定平臺(tái)的:

Platform

private static final Platform PLATFORM = findPlatform();

static Platform get() {
  return PLATFORM;
}

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) {
  }
  return new Platform();
}

從上面的代碼中可以看到,是通過反射判斷有沒有該類來實(shí)現(xiàn)的酗失。若以后在開發(fā)的過程中有需要判斷平臺(tái)的需求义钉,我們可以直接將該段代碼 copy 過來。

接著规肴,在創(chuàng)建 Builder 對(duì)象并進(jìn)行自定義配置后捶闸,我們就要調(diào)用 build() 方法來構(gòu)造出 Retrofit 對(duì)象了。那么拖刃,我們來看下 build() 方法里干了什么:

public Retrofit build() {
  if (baseUrl == null) {
    throw new IllegalStateException("Base URL required.");
  }
  // 默認(rèn)為 OkHttpClient
  okhttp3.Call.Factory callFactory = this.callFactory;
  if (callFactory == null) {
    callFactory = new OkHttpClient();
  }
  // Android 平臺(tái)下默認(rèn)為 MainThreadExecutor
  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);
  // 添加 ExecutorCallAdapterFactory
  adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

  // Make a defensive copy of the converters. 默認(rèn)有 BuiltInConverters
  List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

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

build() 中删壮,做的事情有:檢查配置、設(shè)置默認(rèn)配置兑牡、創(chuàng)建 Retrofit 對(duì)象醉锅。

關(guān)于上面種種奇怪的類,我們先不關(guān)心发绢,因?yàn)橹笪覀冇龅搅嗽俜治觥N覀兿劝涯抗饩劢乖?Retrofit 類上垄琐。

Retrofit

Retrofit 類的構(gòu)造方法沒什么好看的边酒,在這就不講了。

得到 Retrofit 對(duì)象后就是調(diào)用 create(final Class<T> service) 方法來創(chuàng)建我們 API 接口的實(shí)例狸窘。

所以我們需要跟進(jìn) create(final Class<T> service) 中來看下:

  public <T> T create(final Class<T> service) {
    // 校驗(yàn)是否為接口墩朦,且不能繼承其他接口
    Utils.validateServiceInterface(service);
    // 是否需要提前解析接口方法
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    // 動(dòng)態(tài)代理模式
    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);
            }
            // 將接口中的方法構(gòu)造為 ServiceMethod
            ServiceMethod serviceMethod = loadServiceMethod(method);
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

在上面的代碼中,最關(guān)鍵的就是動(dòng)態(tài)代理翻擒。實(shí)際上氓涣,進(jìn)行網(wǎng)絡(luò)操作的都是通過代理類來完成的牛哺。如果對(duì)動(dòng)態(tài)代理不太懂的同學(xué)請(qǐng)自行百度了,這里就不多講了劳吠。

重點(diǎn)就是

// 將接口中的方法構(gòu)造為 ServiceMethod
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);

這三句代碼引润,下面我們著重來看。

在代理中痒玩,會(huì)根據(jù)參數(shù)中傳入的具體接口方法來構(gòu)造出對(duì)應(yīng)的 serviceMethod 淳附。ServiceMethod 類的作用就是把接口的方法適配為對(duì)應(yīng)的 HTTP call 。

  ServiceMethod loadServiceMethod(Method method) {
    ServiceMethod result;
    synchronized (serviceMethodCache) {
      // 先從緩存中取蠢古,若沒有就去創(chuàng)建對(duì)應(yīng)的 ServiceMethod
      result = serviceMethodCache.get(method);
      if (result == null) {
        // 沒有緩存就創(chuàng)建奴曙,之后再放入緩存中
        result = new ServiceMethod.Builder(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

可以看到在內(nèi)部還維護(hù)了一個(gè) serviceMethodCache 來緩存 ServiceMethod ,以便提高效率草讶。我們就直接來看 ServiceMethod 是如何被創(chuàng)建的吧洽糟。

ServiceMethod.Builder

發(fā)現(xiàn) ServiceMethod 也是通過建造者模式來創(chuàng)建對(duì)象的。那就進(jìn)入對(duì)應(yīng)構(gòu)造方法:

public Builder(Retrofit retrofit, Method method) {
  this.retrofit = retrofit;
  this.method = method;
  // 接口方法的注解
  this.methodAnnotations = method.getAnnotations();
  // 接口方法的參數(shù)類型
  this.parameterTypes = method.getGenericParameterTypes();
  // 接口方法參數(shù)的注解
  this.parameterAnnotationsArray = method.getParameterAnnotations();
}

在構(gòu)造方法中沒有什么大的動(dòng)作堕战,那么就單刀直入 build() 方法:

public ServiceMethod build() {
  // 根據(jù)接口方法的注解和返回類型創(chuàng)建 callAdapter
  // 如果沒有添加 CallAdapter 那么默認(rèn)會(huì)用 ExecutorCallAdapterFactory
  callAdapter = createCallAdapter();
  // calladapter 的響應(yīng)類型中的泛型坤溃,比如 Call<User> 中的 User
  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?");
  }

  // 根據(jù)之前泛型中的類型以及接口方法的注解創(chuàng)建 ResponseConverter
  responseConverter = createResponseConverter();

  // 根據(jù)接口方法的注解構(gòu)造請(qǐng)求方法,比如 @GET @POST @DELETE 等
  // 另外還有添加請(qǐng)求頭践啄,檢查url中有無帶?浇雹,轉(zhuǎn)化 path 中的參數(shù)
  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation);
  }

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

  // 若無 body 則不能有 isMultipart 和 isFormEncoded
  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).");
    }
  }

  // 下面的代碼主要用來解析接口方法參數(shù)中的注解,比如 @Path @Query @QueryMap @Field 等等
  // 相應(yīng)的屿讽,每個(gè)方法的參數(shù)都創(chuàng)建了一個(gè) ParameterHandler<?> 對(duì)象
  int parameterCount = parameterAnnotationsArray.length;
  parameterHandlers = new ParameterHandler<?>[parameterCount];
  for (int p = 0; p < parameterCount; p++) {
    Type parameterType = parameterTypes[p];
    if (Utils.hasUnresolvableType(parameterType)) {
      throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
          parameterType);
    }

    Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
    if (parameterAnnotations == null) {
      throw parameterError(p, "No Retrofit annotation found.");
    }

    parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
  }

  // 檢查構(gòu)造出的請(qǐng)求有沒有不對(duì)的地方昭灵?
  if (relativeUrl == null && !gotUrl) {
    throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
  }
  if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
    throw methodError("Non-body HTTP method cannot contain @Body.");
  }
  if (isFormEncoded && !gotField) {
    throw methodError("Form-encoded method must contain at least one @Field.");
  }
  if (isMultipart && !gotPart) {
    throw methodError("Multipart method must contain at least one @Part.");
  }

  return new ServiceMethod<>(this);
}

build() 中代碼挺長的,總結(jié)起來就一句話:就是將 API 接口中的方法進(jìn)行解析伐谈,構(gòu)造成 ServiceMethod 烂完,交給下面的 OkHttpCall 使用。

基本上做的事情就是:

  1. 創(chuàng)建 CallAdapter 诵棵;
  2. 創(chuàng)建 ResponseConverter抠蚣;
  3. 根據(jù) API 接口方法的注解構(gòu)造網(wǎng)絡(luò)請(qǐng)求方法;
  4. 根據(jù) API 接口方法參數(shù)中的注解構(gòu)造網(wǎng)絡(luò)請(qǐng)求的參數(shù)履澳;
  5. 檢查有無異常嘶窄;

代碼中都是注釋,在這里就不詳細(xì)多講了距贷。

ServiceMethod serviceMethod = loadServiceMethod(method); 這句代碼我們看完了柄冲,那么看接下來的 OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);

OkHttpCall

OkHttpCall 的構(gòu)造器中沒什么大動(dòng)作忠蝗,搞不了大事情的:

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

而真正搞事情的是 serviceMethod.callAdapter.adapt(okHttpCall); 這句代碼现横。

ExecutorCallAdapterFactory

在 Retrofit 中默認(rèn)的 callAdapterFactory 是 ExecutorCallAdapterFactory 。我們就進(jìn)入它的 get(Type returnType, Annotation[] annotations, Retrofit retrofit) 看看吧,返回了一個(gè)匿名類 CallAdapter<Object, Call<?>> 戒祠,在其中有 adapt(Call<Object> call) 方法:

  @Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public Call<Object> adapt(Call<Object> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }

可以看到它 return new ExecutorCallbackCall<>(callbackExecutor, call); 骇两。ExecutorCallbackCall 是實(shí)現(xiàn)了 retrofit2.Call ,這里注意下姜盈,是 Retrofit 中的 Call 而不是 OkHttp 中的 Call 低千。使用了裝飾者模式把 retrofit2.Call 又包裝了一層。

在得到了 ExecutorCallbackCall 贩据,我們可以調(diào)用同步方法 execute() 或異步方法 enqueue(Callback<T> callback) 來執(zhí)行該 call 栋操。

ExecutorCallAdapterFactory.ExecutorCallbackCall

那我們就跟進(jìn)同步方法 execute() 吧,異步的 enqueue(Callback<T> callback) 就不看了饱亮。了解過 OkHttp 的同學(xué)應(yīng)該都知道這兩個(gè)方法的區(qū)別矾芙,就是多了異步執(zhí)行和回調(diào)的步驟而已。

@Override 
public Response<T> execute() throws IOException {
  // delegate 就是構(gòu)造器中傳進(jìn)來的 OkHttpCall
  return delegate.execute();
}

所以近上,其實(shí)就是調(diào)用了 OkHttpCallexecute() 方法畏鼓。

所以我們又要回到 OkHttpCall 中了沮脖。

OkHttpCall

  @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 {
          // 根據(jù) serviceMethod 中的眾多數(shù)據(jù)創(chuàng)建出 Okhttp 中的 Request 對(duì)象
          // 注意的一點(diǎn)针余,會(huì)調(diào)用上面的 ParameterHandler.apply 方法來填充網(wǎng)絡(luò)請(qǐng)求參數(shù)
          // 然后再根據(jù) OkhttpClient 創(chuàng)建出 Okhttp 中的 Call
          // 這一步也說明了在 Retrofit 中的 OkHttpCall 內(nèi)部請(qǐng)求最后會(huì)轉(zhuǎn)換為 OkHttp 的 Call
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException e) {
          creationFailure = e;
          throw e;
        }
      }
    }
    // 檢查 call 是否取消
    if (canceled) {
      call.cancel();
    }
    // 執(zhí)行 call 并轉(zhuǎn)換響應(yīng)的 response
    return parseResponse(call.execute());
  }

execute() 做的就是將 Retrofit 中的 call 轉(zhuǎn)化為 OkHttp 中的 call 绍在。

最后讓 OkHttp 的 call 去執(zhí)行。

至此斗锭,Retrofit 的網(wǎng)絡(luò)請(qǐng)求部分源碼已經(jīng)全部解析一遍了地淀。

剩下的就是響應(yīng)部分了,趁熱打鐵岖是。

響應(yīng)源碼解析

我們可以看到 OkHttpCall.execute() 中的最后一句:parseResponse(call.execute())帮毁。

所以對(duì)響應(yīng)的處理就是 parseResponse(okhttp3.Response rawResponse) 這個(gè)方法了。

OkHttpCall

  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();
    // 如果返回的響應(yīng)碼不是成功的話豺撑,返回錯(cuò)誤 Response
    int code = rawResponse.code();
    if (code < 200 || code >= 300) {
      try {
        // Buffer the entire body to avoid future I/O.
        ResponseBody bufferedBody = Utils.buffer(rawBody);
        return Response.error(bufferedBody, rawResponse);
      } finally {
        rawBody.close();
      }
    }
    // 如果返回的響應(yīng)碼是204或者205烈疚,返回沒有 body 的成功 Response
    if (code == 204 || code == 205) {
      return Response.success(null, rawResponse);
    }

    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
      // 將 body 轉(zhuǎn)換為對(duì)應(yīng)的泛型,然后返回成功 Response
      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;
    }
  }

parseResponse(okhttp3.Response rawResponse) 中主要是這句代碼:

T body = serviceMethod.toResponse(catchingBody);

ResponseBody 直接轉(zhuǎn)化為了泛型聪轿,可以猜到這也是 Converter 的功勞爷肝。

ServiceMethod

  T toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
  }

果然沒錯(cuò),內(nèi)部是調(diào)用了 responseConverter 的陆错。

BuiltInConverters

BuiltInConverters 中有好幾種內(nèi)置的 Converter 灯抛。并且只支持返回 ResponseBody 。我們來看下它們的實(shí)現(xiàn):

static final class StreamingResponseBodyConverter
        implements Converter<ResponseBody, ResponseBody> {
    static final StreamingResponseBodyConverter INSTANCE = new StreamingResponseBodyConverter();

    @Override
    public ResponseBody convert(ResponseBody value) throws IOException {
        return value;
    }
}

static final class BufferingResponseBodyConverter
        implements Converter<ResponseBody, ResponseBody> {
    static final BufferingResponseBodyConverter INSTANCE = new BufferingResponseBodyConverter();

    @Override
    public ResponseBody convert(ResponseBody value) throws IOException {
        try {
            // Buffer the entire body to avoid future I/O.
            return Utils.buffer(value);
        } finally {
            value.close();
        }
    }
}

其實(shí)說白了就是將 ResponseBody 轉(zhuǎn)化為對(duì)應(yīng)的數(shù)據(jù)類型了音瓷。比如在 GsonConverterFactory 中就是把 ResponseBody 用 gson 轉(zhuǎn)化為對(duì)應(yīng)的類型牧愁,有興趣的同學(xué)可以自己看下。這里也沒什么神秘的外莲,相信大家都懂的。

到這里就把 Retrofit 響應(yīng)部分的源碼解析完畢了。

大家自行消化一下吧偷线。

我自己也寫得頭暈了磨确。。声邦。笑 cry

Footer

最后乏奥,相信大家已經(jīng)了解了 Retrofit 到底是怎么一回事了。

Retrofit 內(nèi)部訪問網(wǎng)絡(luò)仍然是通過 OkHttp 亥曹,而只是把構(gòu)造請(qǐng)求和響應(yīng)封裝了一下邓了,更加簡(jiǎn)單易用了。

還有媳瞪,看過框架源碼的都知道在源碼中有很多設(shè)計(jì)模式的體現(xiàn)骗炉,比如建造者模式、裝飾者模式以及 OkHttp 中的責(zé)任鏈模式等蛇受。這些也正是值得我們學(xué)習(xí)的地方句葵。

好啦,今天結(jié)束了兢仰。如果有問題的同學(xué)可以留言咯乍丈。

Goodbye

References

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市把将,隨后出現(xiàn)的幾起案子轻专,更是在濱河造成了極大的恐慌,老刑警劉巖察蹲,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件请垛,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡递览,警方通過查閱死者的電腦和手機(jī)叼屠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绞铃,“玉大人镜雨,你說我怎么就攤上這事《酰” “怎么了荚坞?”我有些...
    開封第一講書人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長菲盾。 經(jīng)常有香客問我颓影,道長,這世上最難降的妖魔是什么懒鉴? 我笑而不...
    開封第一講書人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任诡挂,我火速辦了婚禮碎浇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘璃俗。我一直安慰自己奴璃,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開白布城豁。 她就那樣靜靜地躺著苟穆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪唱星。 梳的紋絲不亂的頭發(fā)上雳旅,一...
    開封第一講書人閱讀 49,837評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音间聊,去河邊找鬼攒盈。 笑死,一個(gè)胖子當(dāng)著我的面吹牛甸饱,可吹牛的內(nèi)容都是我干的沦童。 我是一名探鬼主播,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼叹话,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼偷遗!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起驼壶,我...
    開封第一講書人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬榮一對(duì)情侶失蹤氏豌,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后热凹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泵喘,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年般妙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了纪铺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡碟渺,死狀恐怖鲜锚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情苫拍,我是刑警寧澤芜繁,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站绒极,受9級(jí)特大地震影響骏令,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜垄提,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一榔袋、第九天 我趴在偏房一處隱蔽的房頂上張望周拐。 院中可真熱鬧,春花似錦摘昌、人聲如沸速妖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至备恤,卻和暖如春稿饰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背露泊。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來泰國打工喉镰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人惭笑。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓侣姆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親沉噩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子捺宗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

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