那些年我們一起追過(guò)的框架之Retrofit源碼解析

目錄

  1. 前言
  2. 怎樣用Retrofit請(qǐng)求網(wǎng)絡(luò)
  3. Retorfit是如何創(chuàng)建的
  4. 請(qǐng)求接口的實(shí)例是如何創(chuàng)建出來(lái)的
  5. Call到底是什么

5.1 ServiceMethod的創(chuàng)建
5.2 Call的創(chuàng)建
5.3 總結(jié)

  1. Call的enqueue方法到底干了什么
  2. 遺留

1.前言

近些天回想起工作這幾年阁最,感覺(jué)一事無(wú)成戒祠。俗話說(shuō),就算是咸魚(yú)速种,也要做一條有理想的咸魚(yú)姜盈。因此,訂了一個(gè)長(zhǎng)期的計(jì)劃:博客配阵。

為何選擇Retrofit作為人生中的第一篇博客馏颂?

  • Retrofit太火了示血,在最新的2017Android框架中Retrofit排名第一
  • Retrofit是對(duì)OkHttp的封裝。作為一個(gè)封裝庫(kù)救拉,它用了大量的設(shè)計(jì)模式难审,有很多值得借鑒的地方
  • 代碼量少,相對(duì)簡(jiǎn)單

在讀這篇文章前亿絮,假設(shè)你已經(jīng)掌握了

  • Retrofit如何使用

分析源碼一般都有一根主線告喊,本文打算從Retrofit最基本的用法開(kāi)始,一步一步深入了解Retrofit全貌派昧。


2.怎樣用Retrofit請(qǐng)求網(wǎng)絡(luò)

一般我們使用Retrofit需要以下五步:

  • 定義請(qǐng)求接口
  • 創(chuàng)建Retrofit實(shí)例
  • 獲取請(qǐng)求接口的實(shí)例
  • 獲取Call對(duì)象
  • 同步或異步請(qǐng)求
//1.定義請(qǐng)求接口
public interface GitHubService {
    @GET("users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);
}
        //2.創(chuàng)建Retrofit實(shí)例
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.github.com/")
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build();
        //3.通過(guò)Retrofit實(shí)例獲取請(qǐng)求接口實(shí)例(動(dòng)態(tài)代理)
        GitHubService service = retrofit.create(GitHubService.class);
        //4.獲取Call對(duì)象
        Call<List<Repo>> call = service.listRepos("octocat");
        //5.異步請(qǐng)求
        call.enqueue(new Callback<List<Repo>>() {
            @Override
            public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
                //doSometionSuccess
                ...
            }

            @Override
            public void onFailure(Call<List<Repo>> call, Throwable t) {
                //doSometionError
                ...
            }
        });

我們將會(huì)以上述代碼為導(dǎo)火索來(lái)分析源碼黔姜。針對(duì)上述代碼我們先提出以下幾個(gè)問(wèn)題,分別對(duì)應(yīng)代碼注釋中的2 3 4 5對(duì)應(yīng)的代碼:

  • Retrofit是如何創(chuàng)建的
  • 請(qǐng)求接口的實(shí)例是如何創(chuàng)建出來(lái)的
  • Call到底是什么
  • Call的enqueue方法到底干了什么

我會(huì)在每節(jié)的開(kāi)始處附上每個(gè)問(wèn)題對(duì)應(yīng)的代碼


3.Retorfit是如何創(chuàng)建的

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

只要稍微了解Build模式的童鞋都能一眼看出來(lái)蒂萎,Retrofit是通過(guò)Build模式構(gòu)建的秆吵。關(guān)于Retrofit中的那些設(shè)計(jì)模式,我打算另起篇幅介紹五慈,這里就不敘述了纳寂。


    private okhttp3.Call.Factory callFactory;
    private HttpUrl baseUrl;
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    private final List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
    private Executor callbackExecutor;
    private boolean validateEagerly;
    private final Platform platform;
    
    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);
      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);
    }

這里我先簡(jiǎn)單介紹一下各個(gè)參數(shù)的作用:

  • callFactory,前面介紹了Retrofit是對(duì)OkHttp的封裝豺撑,callFactory是Okhttp3提供的類烈疚,用來(lái)自定義OkHttpClient()。
  • callbackExecutor聪轿,請(qǐng)求完成后爷肝,用來(lái)對(duì)回調(diào)過(guò)程進(jìn)行線程控制。默認(rèn)是在主線程中回調(diào)陆错。
  • adapterFactories灯抛,它是CallAdapter.Factory的集合。CallAdapter.Factory是一個(gè)工廠音瓷,用來(lái)生產(chǎn)callAdapter对嚼,callAdapter是對(duì)OkHttpCall的適配。至于OkHttpCall是什么绳慎,我們會(huì)在下面的篇幅中會(huì)介紹纵竖。隨便提一句,Retrofit集成RxJava就是通過(guò)CallAdapter.Factory實(shí)現(xiàn)的杏愤。
  • converterFactories靡砌,它與adapterFactories類似。不同的是珊楼,它生產(chǎn)的Converter可以控制請(qǐng)求結(jié)果的解析通殃。例如,進(jìn)行JSON還是XML解析厕宗。
  • validateEagerly默認(rèn)為false画舌,validateEagerly參數(shù)如果我們?cè)谂渲玫臅r(shí)候設(shè)置為true的時(shí)候堕担,程序是在做一個(gè)預(yù)處理的操作,會(huì)提前解析請(qǐng)求接口中定義的方法曲聂,生產(chǎn)MethodHandler并緩存霹购。
  • Platform,Retrofit希望是一個(gè)跨平臺(tái)的網(wǎng)絡(luò)請(qǐng)求庫(kù)朋腋,因此它定義了Platform接口厕鹃。每個(gè)平臺(tái)都提供了默認(rèn)的callbackExecutor與CallAdapter.Factory實(shí)現(xiàn)。

platform源碼

static class Android extends Platform {
    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

    @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
      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);
      }
    }

4.請(qǐng)求接口的實(shí)例是如何創(chuàng)建出來(lái)的

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

我們看一下Retrofit的create方法乍丈。

    public <T> T create(final Class<T> service) {

        Utils.validateServiceInterface(service);

        if (validateEagerly) {
            eagerlyValidateMethods(service);
        }
        //動(dòng)態(tài)代理,返回的是T類型的對(duì)象把将。
        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);
                    }
                });
    }


我們先暫時(shí)不去分析eagerlyValidateMethods(service)方法轻专,因?yàn)樗雌饋?lái)即使validateEagerly為false,我們的程序仍能正常運(yùn)行察蹲。
Proxy.newProxyInstance()生成了一個(gè)動(dòng)態(tài)代理對(duì)象请垛,而InvocationHandler可以攔截這個(gè)對(duì)象的所有方法。這么說(shuō)可能有些抽象洽议,你可以簡(jiǎn)單的認(rèn)為以下這兩段代碼等價(jià)宗收。

    GitHubService service = retrofit.create(GitHubService.class)

    GitHubService service = new GitHubService() {
    //創(chuàng)建InvocationHandler對(duì)象
        InvocationHandler invocationHandler = 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);
            }
        };
        
    //調(diào)用listRepos方法其實(shí)是調(diào)用InvocationHandler的invoke方法,參數(shù)分別為:當(dāng)前GitHubService對(duì)象亚兄,listRepos()方法對(duì)象混稽,listRepos()傳入的參數(shù)
        @Override
        public Call<List<String>> listRepos(@Path("user") String user) {
            return invocationHandler.invoke(this, GitHubService.class.getMethod("listRepos"), user);
        }
    }

5.Call到底是什么

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

service.listRepos()方法其實(shí)調(diào)用了invocationHandler的invoke()方法。其核心代碼僅有三行审胚。

  //ServiceMethod的創(chuàng)建
  ServiceMethod serviceMethod = loadServiceMethod(method);
  //Call的創(chuàng)建 
  OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
  return serviceMethod.callAdapter.adapt(okHttpCall);

這部分將分為兩部分介紹匈勋,ServiceMethod的創(chuàng)建和Call的創(chuàng)建。

5.1 ServiceMethod的創(chuàng)建

  ServiceMethod<?, ?> loadServiceMethod(Method method) {
    ServiceMethod<?, ?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = new ServiceMethod.Builder<>(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

loadServiceMethod是為了創(chuàng)建ServiceMethod膳叨,然后將其放入的緩存中洽洁,緩存的Key為Method對(duì)象。ServiceMethod同樣是通過(guò)Builder模式創(chuàng)建的菲嘴。上述方法中的this指的是當(dāng)前Retrofit對(duì)象饿自。因此可以推測(cè)ServiceMethod.Builder持有了Retrofit對(duì)象和Method對(duì)象的引用。事實(shí)也的確如此龄坪,如下:

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

這段代碼主要是保存了Retrofit的實(shí)例對(duì)象昭雌,method對(duì)象,method上的注解悉默,method參數(shù)注解城豁,method參數(shù)類型。

    public ServiceMethod build () {
       
        callAdapter = createCallAdapter();
        responseType = callAdapter.responseType();
        responseConverter = createResponseConverter();

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

        // ......

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

        return new ServiceMethod<>(this);
    }

createCallAdapter主要是通過(guò)Builder中保存的retrofit對(duì)象抄课,找到CallAdapterFactory集合唱星,遍歷集合雳旅,根據(jù)Method的返回值類型和Method上的注解,找到合適的callAdapter间聊。callAdapter是一個(gè)接口攒盈,定義了兩個(gè)方法:Type responseType(),返回值類型哎榴;<R> T adapt(Call<R> call)型豁,用來(lái)返回代理對(duì)象。我們看一下Platform中默認(rèn)提供的CallAdapterFactory尚蝌。

final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
  final Executor callbackExecutor;

  ExecutorCallAdapterFactory(Executor callbackExecutor) {
    this.callbackExecutor = callbackExecutor;
  }

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

這樣ServiceMethod內(nèi)部就創(chuàng)建了一個(gè)匿名的CallAdapter對(duì)象迎变。

createResponseConverter與createCallAdapter過(guò)程類似,不再贅述飘言。build()方法中其余的代碼主要是通過(guò)解析注解和拼接參數(shù)來(lái)生成Http請(qǐng)求數(shù)據(jù)衣形,如:get、post姿鸿、delete等方法谆吴、請(qǐng)求Url、參數(shù)苛预。至此句狼,ServiceMethod中擁有了callAdapter、responseConverter以及和網(wǎng)絡(luò)請(qǐng)求有關(guān)的url热某、parameter腻菇、method等。loadServiceMethod介紹完畢苫拍。

5.2 Call的創(chuàng)建

我們看一下這兩段代碼的意思

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

OkHttpCall是Call的實(shí)現(xiàn)類芜繁,它有一個(gè)ServiceMethod對(duì)象和一個(gè)請(qǐng)求參數(shù)的數(shù)組。如下:

  final class OkHttpCall<T> implements Call<T> {
      //構(gòu)造方法
      OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) {
      this.serviceMethod = serviceMethod;
      this.args = args;
    }
  }

我們?cè)?ServiceMethod的創(chuàng)建 中介紹了serviceMethod.callAdapter是一個(gè)匿名類绒极。它的adapt方法返回了一個(gè)ExecutorCallbackCall對(duì)象骏令。

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

這是典型的裝飾器模式,ExecutorCallbackCall是對(duì)OkHttpCall對(duì)象的裝飾垄提。它內(nèi)部的delegate對(duì)象就是上面分析的OkHttpCall對(duì)象榔袋。callbackExecutor是我們?cè)?* Retorfit是如何創(chuàng)建的 **中介紹的callbackExecutor對(duì)象。Platform提供了默認(rèn)實(shí)現(xiàn)MainThreadExecutor铡俐。MainThreadExecutor很簡(jiǎn)單凰兑,它將接收到的Runnable對(duì)象交給主線程的Handler去處理。

    static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override public void execute(Runnable r) {
        handler.post(r);
      }
    }

5.3 總結(jié)

我們返回的call對(duì)象其實(shí)是一個(gè)ExecutorCallbackCall對(duì)象审丘,它實(shí)現(xiàn)了Call接口吏够,是對(duì)OkHttpCall的裝飾,并且內(nèi)部持有了callbackExecutor對(duì)象。OkHttpCall也實(shí)現(xiàn)了Call接口锅知,它有持有了一個(gè)ServiceMethod對(duì)象和一個(gè)請(qǐng)求參數(shù)的數(shù)組的引用播急。ServiceMethod封裝了請(qǐng)求的細(xì)節(jié),如get售睹、post桩警、delete等方法、請(qǐng)求Url昌妹、參數(shù)等捶枢,同時(shí)callAdapter和responseConverter也是在ServiceMethod中創(chuàng)建的。


6.Call的enqueue方法到底干了什么

 call.enqueue(new Callback<List<Repo>>() {
            @Override
            public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
                //doSometionSuccess
                ...
            }

            @Override
            public void onFailure(Call<List<Repo>> call, Throwable t) {
                //doSometionError
                ...
            }
        });

在上節(jié)中飞崖,我們知道了返回的Call實(shí)例是ExecutorCallbackCall類型的對(duì)象烂叔。那我們看一下ExecutorCallbackCall的enqueue()方法。

   public void enqueue(final Callback<T> callback) {

        delegate.enqueue(new Callback<T>() {
            @Override public void onResponse(Call<T> call, final Response<T> response) {
                callbackExecutor.execute(new Runnable() {
                    @Override public void run() {
                        if (delegate.isCanceled()) {
                            // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
                            callback.onFailure(ExecutorCallAdapterFactory.ExecutorCallbackCall.this, new IOException("Canceled"));
                        } else {
                            callback.onResponse(ExecutorCallAdapterFactory.ExecutorCallbackCall.this, response);
                        }
                    }
                });
            }
            @Override public void onFailure(Call<T> call, final Throwable t) {
                callbackExecutor.execute(new Runnable() {
                    @Override public void run() {
                        callback.onFailure(ExecutorCallAdapterFactory.ExecutorCallbackCall.this, t);
                    }
                });
            }
        });
    }

我們先來(lái)介紹一下這段代碼中出現(xiàn)的變量固歪,然后再來(lái)分析整段代碼的含義长已。

  • delegate是上一節(jié)介紹的OkHttpCall對(duì)象。
  • callbackExecutor是上一節(jié)介紹的MainThreadExecutor對(duì)象昼牛。

這段代碼更能說(shuō)明ExecutorCallbackCall采用了裝飾模式,它生成了一個(gè)新的Callback對(duì)象康聂。這個(gè)新Callback有兩個(gè)作用贰健。一,將回調(diào)切換到callbackExecutor中執(zhí)行恬汁。二伶椿,增加了取消操作。最終氓侧,這個(gè)新的Callback對(duì)象交給OkHttpCall對(duì)象的enqueue方法去處理脊另。

public void enqueue(final Callback<T> callback) {
    checkNotNull(callback, "callback == null");

    okhttp3.Call call;
    Throwable failure;

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

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

    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }

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

    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }
        callSuccess(response);
      }

      @Override public void onFailure(okhttp3.Call call, IOException e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    });
  }

OkHttpCall是對(duì)Okhttp3.Call的包裝,OkHttpCall內(nèi)部有一個(gè)類型為Okhttp3.Call的rawCall對(duì)象约巷。rawCall的創(chuàng)建偎痛,是通過(guò)Retrofit初始化時(shí),傳入的OkHttpClient創(chuàng)建的(若為空独郎,系統(tǒng)提供了默認(rèn)的)踩麦。在enqueue方法中利用rawCall對(duì)象發(fā)起請(qǐng)求。請(qǐng)求成功后氓癌,會(huì)通過(guò)parseResponse(rawResponse)方法對(duì)原始數(shù)據(jù)進(jìn)行解析谓谦。
而parseResponse()的核心代碼只有一句話。

 T body = serviceMethod.toResponse(catchingBody);

不錯(cuò)贪婉,它調(diào)用了serviceMethod的toResponse方法反粥。而serviceMethod的toResponse方法也只有一句話。

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

還記得我們?cè)?ServiceMethod的創(chuàng)建 提到的,和callAdapter創(chuàng)建過(guò)程類似的responseConverter嗎才顿?沒(méi)錯(cuò)莫湘,就是它。


7.遺留

我們?cè)?* 請(qǐng)求接口的實(shí)例是如何創(chuàng)建出來(lái)的 ** 這節(jié)中遺留下一段代碼

  if (validateEagerly) {
        eagerlyValidateMethods(service);
  }

我們現(xiàn)在回過(guò)頭來(lái)看一下這段代碼

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

loadServiceMethod是不是很熟悉娜膘。不要告訴我你已經(jīng)忘了!!! 好吧逊脯,那我來(lái)告訴你,ServiceMethod的創(chuàng)建 中我們最先介紹的就是這個(gè)方法竣贪。eagerlyValidateMethods是一個(gè)對(duì)service中的方法提前解析军洼,將其緩存的過(guò)程。所以我們可以通過(guò)設(shè)置validateEagerly=true來(lái)提高請(qǐng)求效率演怎。


哇匕争,終于完了,寫(xiě)博客是真的累啊
真的累啊
真累啊
累啊
累R8噬!!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末歹叮,一起剝皮案震驚了整個(gè)濱河市跑杭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌咆耿,老刑警劉巖德谅,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異萨螺,居然都是意外死亡窄做,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)慰技,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)椭盏,“玉大人,你說(shuō)我怎么就攤上這事吻商√图眨” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵艾帐,是天一觀的道長(zhǎng)蚯舱。 經(jīng)常有香客問(wèn)我,道長(zhǎng)掩蛤,這世上最難降的妖魔是什么枉昏? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮揍鸟,結(jié)果婚禮上兄裂,老公的妹妹穿的比我還像新娘句旱。我一直安慰自己,他們只是感情好晰奖,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布谈撒。 她就那樣靜靜地躺著,像睡著了一般匾南。 火紅的嫁衣襯著肌膚如雪啃匿。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,749評(píng)論 1 289
  • 那天蛆楞,我揣著相機(jī)與錄音溯乒,去河邊找鬼。 笑死豹爹,一個(gè)胖子當(dāng)著我的面吹牛裆悄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播臂聋,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼光稼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了孩等?” 一聲冷哼從身側(cè)響起艾君,我...
    開(kāi)封第一講書(shū)人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎肄方,沒(méi)想到半個(gè)月后腻贰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扒秸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了冀瓦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伴奥。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖翼闽,靈堂內(nèi)的尸體忽然破棺而出拾徙,到底是詐尸還是另有隱情,我是刑警寧澤感局,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布尼啡,位于F島的核電站,受9級(jí)特大地震影響询微,放射性物質(zhì)發(fā)生泄漏崖瞭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一撑毛、第九天 我趴在偏房一處隱蔽的房頂上張望书聚。 院中可真熱鬧,春花似錦、人聲如沸雌续。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)驯杜。三九已至受啥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鸽心,已是汗流浹背滚局。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留再悼,地道東北人核畴。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像冲九,于是被迫代替她去往敵國(guó)和親谤草。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

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