流行框架源碼分析(9)-Retrofit2源碼解析

主目錄見:Android高級進階知識(這是總目錄索引)
?我們知道Retrofit2是基于OkHttp的一個Restful Api請求工具,它是一個類型安全的http客戶端請求工具(Type-safe HTTP client for Android and Java ),上一篇文章[OkHttp源碼分析]我們已經說了OkHttp的機制,這里我們就來說說Retrofit2了,從功能上來說,Retrofit有點類似Volley,但是使用方式上相對而言Retrofit會更簡單,其實Retrofit的源代碼還是比較簡單的,我們就直接開始吧。

retrofit

一.目標

?我們說Retrofit2的一個初衷是他現在用的比較多,而且能跟比較火的rxjava結合使用,同時Retrofit2內部使用了代理,同時Api的使用通過注解來完成。所以我們今天的目標如下:
1.了解代理的使用場景;
2.通過源碼來了解Retrofit的使用。

二.源碼分析

首先我們來看看Retrofit基本的用法吧,這也是我們看代碼的老套路了:
1.根據Api創建一個java接口,用注解描述方法

public interface PhoneService {
    @GET("/")
    Call<PhoneResult>getResult(@Query("mobile") String phone);
}

2.新建一個Retrofit對象,并指定服務器URL

        Retrofit retrofit=new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl(BASE_URL)
                .build();

3.創建接口對象例如這里的PhoneService,然后得到Call對象

        PhoneService service=retrofit.create(PhoneService.class);
        Call<PhoneResult>call=service.getResult(etPhone.getText().toString());

4.利用Call對象獲取數據,有異步和同步之分,這里以異步為例

        call.enqueue(new Callback<PhoneResult>() {
            @Override
            public void onResponse(Call<PhoneResult> call, Response<PhoneResult> response) {
                //4.處理結果
                if(response.isSuccessful()){
                    PhoneResult result=response.body();
                    if (result!=null){
                        tvLocal.setText(result.getData().getProvince()+".."+result.getData().getCity());
                        Toast.makeText(MainActivity.this,"網絡訪問成功!"+result,Toast.LENGTH_LONG).show();
                    }
                }
            }

            @Override
            public void onFailure(Call<PhoneResult> call, Throwable t) {
                Toast.makeText(MainActivity.this,"網絡訪問失敗!"+t,Toast.LENGTH_LONG).show();
            }
        });

我們看到使用的流程還是清晰簡單的,在構建Retrofit對象的時候我們看到添加了一個ConverterFactory,同時如果我們跟Rxjava集成還需要添加一個CallAdapterFactory為RxJavaCallAdapterFactory。這個地方就記住就行,免得后面提到不知道哪里出現的。

1.Retrofit create

這個就是獲取到一個PhoneService對象了,我們來看看怎么就通過一個PhoneService.class參數就得到這個對象呢?我們直接來看create方法干了什么:

  @SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
  public <T> T create(final Class<T> service) {
//驗證這個class里面是否有接口
    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, @Nullable 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);
            }
//如果這個方法是默認方法(這個默認方法是在java8中引入的),則也直接調用
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

我們看到這里使用了java的動態代理,java中的動態代理可以攔截方法的調用來添加額外的功能,這里同樣,調用參數class里面方法的時候可以根據里面的注解等信息來拼接我們需要的請求。具體我們來看ServiceMethod的結構:

final class ServiceMethod<R, T> {
  // Upper and lower characters, digits, underscores, and hyphens, starting with a character.
  static final String PARAM = "[a-zA-Z][a-zA-Z0-9_-]*";
  static final Pattern PARAM_URL_REGEX = Pattern.compile("\\{(" + PARAM + ")\\}");
  static final Pattern PARAM_NAME_REGEX = Pattern.compile(PARAM);

  final okhttp3.Call.Factory callFactory;
//這個設計主要是針對rxjava設計的,為了將一個Call轉化為另外一個對象
  final CallAdapter<R, T> callAdapter;
//服務器基地址
  private final HttpUrl baseUrl;
//返回數據轉化器
  private final Converter<ResponseBody, R> responseConverter;
//http請求的方法
  private final String httpMethod;
//相對地址
  private final String relativeUrl;
//請求頭部
  private final Headers headers;
//請求的contentType
  private final MediaType contentType;
  private final boolean hasBody;
  private final boolean isFormEncoded;
  private final boolean isMultipart;
  private final ParameterHandler<?>[] parameterHandlers;
....
}

我們看到這個類主要是封裝了方法的一些信息的,很顯然,loadServiceMethod()方法就是為了解析調用的method的信息然后封裝到ServiceMethod,我們具體看下這個方法:

ServiceMethod<?, ?> loadServiceMethod(Method method) {
//serviceMethodCache這是一個key為Method,value為ServiceMethod的Map作為緩存使用
    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;
  }

我們看到這里首先會看緩存里面是否已經有這個ServiceMethod對象,如果沒有則我們會調用ServiceMethod.Builder#build方法來進行獲取,首先我們看ServiceMethod.Builder的構造函數:

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

我們看到這個構造函數主要是獲取方法,方法的注解,方法的參數類型,方法參數的注解等信息。然后我們來看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);
      }
//省略一些判斷
......
      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()做了什么:

  private CallAdapter<T, R> createCallAdapter() {
//返回方法的泛型返回類型,因為Retrofit是類型安全的
      Type returnType = method.getGenericReturnType();
      if (Utils.hasUnresolvableType(returnType)) {
        throw methodError(
            "Method return type must not include a type variable or wildcard: %s", returnType);
      }
      if (returnType == void.class) {
        throw methodError("Service methods cannot return void.");
      }
//返回方法的所有注解
      Annotation[] annotations = method.getAnnotations();
      try {
        //noinspection unchecked
        return (CallAdapter<T, R>) 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);
      }
    }

我們看到這個方法后面調用retrofit的callAdapter方法,那么我們來看這個方法做了什么:

public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
  }

我們看到這個方法又調用了nexCallAdapter方法,我們同樣直接跟進去:

  public CallAdapter<?, ?> nextCallAdapter(@Nullable 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;
      }
    }
//省略一個未找到callAdapter的錯誤字符串拼接
......
  }

我們看到這段代碼會遍歷adapterFactories,然后取出CallAdapter對象,但是這個adapterFactories有哪些CallAdapter.Factory呢,我們知道我們如果集成rxjava的時候會調用addCallAdapterFactory()方法,這個方法在Retrofit的Builder中,其實就是為了給adapterFactories添加CallAdapter.Factory的,但是如果沒有設置的話那又是什么呢?我們來看Retrofit的Builder里面:

  public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
      adapterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }

 public Retrofit build() {
...
   // 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));
.....
}

#Platform
  @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }

從這里可以看出,我們可以自己通過addCallAdapterFactory添加CallAdapter.Factory對象,如果沒有設置系統會使用默認的CallAdapter.Factory為ExecutorCallAdapterFactory。這樣我們就知道build()方法里面的createCallAdapter()方法返回的就是ExecutorCallAdapterFactory對象通過get獲取得到的CallAdapter了。我們繼續看ExecutorCallAdapterFactory的get()方法返回的是什么:

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

我們看到這個get返回的就是一個CallAdapter對象,同時這個類里面有兩個方法responseType返回的是方法的返回值的泛型類型以及一個adapt返回ExecutorCallbackCall對象。這樣我們得到CallAdapter對象了,我們回到ServiceMethod#build()方法里面看到程序里面又調用了createResponseConverter()方法,這個方法是干嘛的呢?這個方法基本與CallAdapter的獲取過程是一樣的,我們這里就不贅述了。接下來我們看buid方法中的parseMethodAnnotation方法:

  private void parseMethodAnnotation(Annotation annotation) {
      if (annotation instanceof DELETE) {
        parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
      } else if (annotation instanceof GET) {
        parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
      } else if (annotation instanceof HEAD) {
        parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
        if (!Void.class.equals(responseType)) {
          throw methodError("HEAD method must use Void as response type.");
        }
      } else if (annotation instanceof PATCH) {
        parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
      } else if (annotation instanceof POST) {
        parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
      } else if (annotation instanceof PUT) {
        parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
      } else if (annotation instanceof OPTIONS) {
        parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
      } else if (annotation instanceof HTTP) {
        HTTP http = (HTTP) annotation;
        parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
      } else if (annotation instanceof retrofit2.http.Headers) {
        String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
        if (headersToParse.length == 0) {
          throw methodError("@Headers annotation is empty.");
        }
        headers = parseHeaders(headersToParse);
      } else if (annotation instanceof Multipart) {
        if (isFormEncoded) {
          throw methodError("Only one encoding annotation is allowed.");
        }
        isMultipart = true;
      } else if (annotation instanceof FormUrlEncoded) {
        if (isMultipart) {
          throw methodError("Only one encoding annotation is allowed.");
        }
        isFormEncoded = true;
      }
    }

這個方法主要是根據請求方法上面注解來獲取請求地址,請求的參數,請求方法名信息。然后我們繼續看build()方法的下一個方法parseParameter,這個方法我們這里說一下他的作用,這個方法主要是解析方法參數的注解等信息,然后封裝到ParameterHandler對象里面去,最后每個方法參數對應一個ParameterHandler對象放入parameterHandlers數組里面。到這里我們ServiceMethod的build()方法就完成了,最后返回了ServiceMethod對象。然后我們回歸到Retrofit的create方法,我們知道我們程序會把ServiceMethod對象和方法參數args放進OkHttpCall對象里面,我們看下這個構造函數:

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

最后Retrofit#create會調用serviceMethod.callAdapter.adapt(okHttpCall)方法進行進一步封裝,我們知道這里的callAdapter就是我們前面說的ExecutorCallAdapterFactory中get()方法返回的CallAdapter,我們看下adapt方法干了什么呢:

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

我們看到這個方法很簡單,其實就是將我們的OkHttpCall對象傳給了ExecutorCallbackCall對象了,那么我們最后知道Retrofit#create方法最終返回的就是ExecutorCallbackCall對象。

2.ExecutorCallbackCall

很明顯,根據我們用法知道,我們這里用異步請求距離,我們調用Call對象的enqueue方法,也就是說我們調用的是ExecutorCallbackCall的enqueue,我們看下ExecutorCallbackCall類的這個方法:

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

      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(ExecutorCallbackCall.this, new IOException("Canceled"));
              } else {
                callback.onResponse(ExecutorCallbackCall.this, response);
              }
            }
          });
        }

        @Override public void onFailure(Call<T> call, final Throwable t) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              callback.onFailure(ExecutorCallbackCall.this, t);
            }
          });
        }
      });
    }

這里面的delegate就是前面傳進去的OkHttpCall對象,所以程序會調用OkHttpCall的enqueue方法:

 @Override 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 {
//獲取okhttp3.Call對象
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          failure = creationFailure = t;
        }
      }
    }

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

    if (canceled) {
      call.cancel();
    }
//調用okhttp3.Call中的enqueue方法
    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) {
        callFailure(e);
      }

      private void callFailure(Throwable e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }

      private void callSuccess(Response<T> response) {
        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    });
}

這個代碼看著很長,其實很簡單,主要就兩處代碼,首先我們看第一個注釋地方獲取okhttp3.Call對象的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;
  }

我們看到這個方法主要是先獲取到一個request對象,然后利用這個request對象來獲取okhttp3.Call對象,首先我們來看這個request對象怎么獲取,我們跟進來ServiceMethod里面的toRequest方法:

  Request toRequest(@Nullable Object... args) throws IOException {
//實例化一個requestBuilder對象
    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;

    int argumentCount = args != null ? args.length : 0;
    if (argumentCount != handlers.length) {
      throw new IllegalArgumentException("Argument count (" + argumentCount
          + ") doesn't match expected count (" + handlers.length + ")");
    }
//將請求方法里面的參數添加到requestBuilder中
    for (int p = 0; p < argumentCount; p++) {
      handlers[p].apply(requestBuilder, args[p]);
    }

    return requestBuilder.build();
  }

這個方法其實就是根據前面得到的ServiceMethod里面得到的請求方法的請求地址,請求方法,請求的contentType,請求參數等等信息得到一個Request對象。然后我們來看serviceMethod對象中的callFactory到底是什么。我們首先看到callFactory這個變量是在ServiceMethod構造函數的時候構造的:

  ServiceMethod(Builder<R, T> builder) {
    this.callFactory = builder.retrofit.callFactory();
}

可以看到,這個callFactory又是retrofit里面的callFactory()方法返回的,我們看下:

  public okhttp3.Call.Factory callFactory() {
    return callFactory;
  }

其實這個方法就是直接返回的Retroft這個類中的callFactory對象,那么這個到底是什么呢?其實在創建Retrofit對象的時候即調用build()方法的時候已經創建了:

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

如果我們沒有設置這個okhttp3.Call.Factory,我們就默認是OkHttpClient,也就是說Retrofit2默認使用的是OkHttp來訪問網絡,所以我們就是調用的OkHttpClient的newCall方法來得到一個okhttp3.Call對象。也就是說最后調用call.enqueue()方法其實就是調用的OkHttp來訪問的網絡。

3.parseResponse

我們看到調用OkHttp訪問網絡成功后會調用parseResponse(rawResponse)方法來解析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();
    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();
      }
    }

    if (code == 204 || code == 205) {
      rawBody.close();
      return Response.success(null, rawResponse);
    }

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

我們看到前面會進行一些請求返回碼的判斷來做相應的處理,最后最終會調用ServiceMethod的toResponse()方法來返回body的內容:

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

我們看到了responseConverter,這個對象我們應該很熟悉了,這個對象就是我們之前用addConverterFactory添加進來的GsonConverterFactory中獲取到的GsonResponseBodyConverter呀,所以我們直接看GsonResponseBodyConverter的convert方法:

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

如果了解gson的大家都知道TypeAdapter的read()方法將會傳入一個JsonReader對象實例并返回反序列化的對象。也就是說會把一個json字符串直接反序列化為一個對象,這樣的T類型就會是返回的一個具體的對象。

通過上面的數據解析我們也可以看出,我們還可以自定義一個自己的解析器類似GsonConverterFactory,然后分別重寫responseBodyConverter()方法和requestBodyConverter()方法,分別定義兩個返回數據的解析器和請求數據的解析器類似GsonResponseBodyConverter和GsonRequestBodyConverter。

總結:Retrofit的整體流程還是比較簡單的,通過源碼的學習相信大家應該更好地掌握了他的用法以及一些設計思想,還是非常巧妙的,希望大家有享受到這里面的代碼分析過程。如果不懂代理,注解,OkHttp等知識的請了解清楚哈。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,125評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,506評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,614評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,402評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,934評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,168評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,690評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,596評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,027評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,404評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,662評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,398評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,743評論 2 370

推薦閱讀更多精彩內容