我了解的Retrofit2

一.概述

我想作為一個Android開發(fā)如果沒有聽過Retrofit網(wǎng)絡(luò)請求框架蕉饼,那么他是與時代脫節(jié)的,你隨便在gayhub上找一個開源的Android項目都能發(fā)現(xiàn)Retrofit的身影碘梢。在介紹Retrofit之前,大家還是需要知道okhttp的基本使用方式的碰镜。
okhttp使用很簡單鲫惶,主要分為同步請求和異步請求:

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
  • 同步請求,即等待請求結(jié)果返回岗憋,然后對結(jié)果進(jìn)行解析歌亲,主要流程:
Response response = client.newCall(request).execute();

然后對返回的結(jié)果Request進(jìn)行解析。

  • 異步請求澜驮,即回調(diào)式請求陷揪,結(jié)果返回后進(jìn)行回調(diào),不會阻塞當(dāng)前線程的執(zhí)行杂穷。主要流程:
Call call = client.newCall(request);
call.enqueue(new Callback() {
    @Override
    public void onResponse(Response response) throws IOException {
        //get result from response.
    }
    @Override
    public void onFailure(Request arg0, IOException arg1) {
    
    } 
});

可以看到悍缠,兩種操作方式,都是對返回的Response進(jìn)行包括io操作耐量、結(jié)果轉(zhuǎn)換等解析操作飞蚓。你可以把okhttp就當(dāng)成類似HttpURLConnection一樣的Api,他們的功能是一樣的都是負(fù)責(zé)網(wǎng)絡(luò)請求廊蜒。如果想對Okhttp進(jìn)一步了解的可以參考:Android OkHttp完全解析 是時候來了解OkHttp了趴拧。

二.Retrofit的使用

既然都會使用okhttp了,那么使用Retrofit就很容易了山叮。網(wǎng)上教程一大堆著榴,這邊主要羅列基本的3個操作:

  • 1.創(chuàng)建接口類,其中每個方法都是一個對應(yīng)的網(wǎng)絡(luò)請求屁倔。
public interface API {
    @POST("/load")
    Call<Rep> load();
    
    @Post("/test")
    Call<Rep> down();
}
  • 2.創(chuàng)建Retrofit對象脑又,方式和Okhttp一樣是通過Builder模式進(jìn)行配置生成對象,其中baseUrl表示表示完整的url前半段,和API中的注解合并成最終的url锐借。
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("http://com.seu.test/")
    .build();
  • 3.創(chuàng)建異步請求
Call<Rep> call = retrofit.create(API.class).load();
call.enquque(new Callback<Rep> {
    @Override
    public void onResponse(Call<Rep> call, Response<Rep> response) {
        //get result from response
    }
 
    @Override
    public void onFailure(Call<Rep> call, Throwable t) {
 
    }
});

是不是看去和Okhttp的異步請求一模一樣问麸。你猜對了,Retrofit就是對Okhttp的網(wǎng)絡(luò)請求的封裝钞翔。如果你現(xiàn)在還不了解Retrofit的用途严卖,那我還能怎么辦呢。Retrofit2.0使用詳解布轿,我也只能幫到這了哮笆。

三.Retrofit源碼分析

注解Annotation

通過查看源碼可以知道在retrofit2.http目錄下面都是Java注解類俺亮,其中包括熟悉的GETPOST等疟呐。本文結(jié)合GET進(jìn)行入門式介紹脚曾。
Java中包括4種元注解(用于自定義注解的注解):

  • Documented,如果定義了該注解启具,則通過javadoc后本讥,該注解的信息可以在docs中顯示(對于開發(fā)來說,沒啥用鲁冯,不行就在自定義注解中直接加上就行)拷沸。
  • Inherited 定義該注解是否可以在子類中被繼承。一般該注解的使用也不會很多薯演,至今沒用過繼承的注解撞芍。
  • Retention 定義了該注解的存活周期。這個是我見過用處比較大的注解了跨扮,但是在我解釋了他的用處以后序无,你也會覺得so easy。Retention包括三部分:
Rentention 的三個enum屬性
SOURCE: 說明該自定義注解衡创,只存在與XX.java中帝嗡,在編譯后的XX.class都是找不到的。比如@Override璃氢,可以通過jd-gui來查看XX.class文件哟玷,確實找不到該注解,該注解是在虛擬機層面進(jìn)行解析的一也,實際自定義注解中大概也不會用到該類別巢寡。
CLASS : 存在于XX.java和XX.class文件中,但是在運行時Java虛擬機會忽略該注釋椰苟,只有在編譯階段會讀取到該注解抑月,在運行時通過反射是獲取不到該注解的,可以查看Butter Knife尊剔,他就是編譯時注解爪幻。
RUNTIME: 和class屬性一樣,都存在于源文件以及編譯生成的class文件中须误,不同的是在運行時可以得到該注解,即可以運行時可以通過反射得到仇轻。
  • Target表示該注解可以用在哪個地方京痢,比如:用于屬性字段、用于方法聲明篷店、用于包聲明祭椰、用于接口聲明等等臭家,詳細(xì)的可以參考ElementType。

在了解了Annotation的元注解以后方淤,我們來解釋GET

//還不是小兒科钉赁,直接忽略
@Documented
//說明該注解是使用在方法上的,且只能用在方法上
@Target(METHOD)
//說明該注解是運行時注解携茂,可以通過反射得到
@Retention(RUNTIME)
public @interface GET {
  //需要有一個屬性
  String value() default "";
}

怎么樣你踩,一個自定義的注解就可以完成了,媽媽再也不用擔(dān)心我看不懂注解咯讳苦。其實所有的自定義注解都是照貓畫虎带膜,沒啥困難的。還想深入全名學(xué)習(xí)注解的可以參考:Java注解(Annotation)鸳谜,保證藥到病除膝藕。到現(xiàn)在為止,retrofit2.http下面所有的類基本都可以知道它們的真實含義了吧咐扭。

動態(tài)代理

在將ServiceMethod之前還是需要稍微介紹下Java的動態(tài)代理技術(shù):動態(tài)代理是jdk封裝好的比較好用的類代理生成方式芭挽,Proxy.newProxyInstance有三個參數(shù):

  • ClassLoader,這個比較通用蝗肪,直接用類本身的Classloader就可以览绿。
  • Class<?>[] interfaces,表示該生成代理類要實現(xiàn)的接口功能穗慕。
  • InvocationHandler h饿敲,接口功能的真正實現(xiàn)是h。

Proxy.newProxyInstance是通過反射的方式生成代理類逛绵,通過源碼分析可以看到怀各,Class對象是通過ProxyGenerator生成的字節(jié)碼,不信的話你可以通過ProxyGenerator生成的byte[]保存到xx.class文件中术浪,看看是不是把所有的功能都讓h來完成的瓢对,可以參考:JDK動態(tài)代理的實現(xiàn)及原理,保證讓你對動態(tài)代理有比較深刻的理解胰苏。
看到了沒有硕蛹,說話算話,真的很簡單的介紹了動態(tài)代理硕并。

開刀Retrofit.java

Retrofit是通過Builder模式生成的對象法焰,所以直接對Retrofit.Builder類進(jìn)行解析,這里只羅列來了屬性和build()倔毙,屬性設(shè)置方法略(沒啥特別的埃仪,都是一堆類似set方法):

public static final class Builder {
    //Retrofit運行的平臺:具體實現(xiàn)類比較簡單,其中包括了Android平臺陕赃、IOS平臺卵蛉、還有java8平臺颁股。這里你就直接理解為Android平臺吧。
    private Platform platform;
    //該接口直接實現(xiàn)類為OkHttpClient傻丝,相信我甘有。
    private okhttp3.Call.Factory callFactory;
    private HttpUrl baseUrl;
    private List<Converter.Factory> converterFactories = new ArrayList<>();
    private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
    //網(wǎng)絡(luò)請求返回后是不在主線程的,那么是不是需要有一個主線程的executor來執(zhí)行ui操作葡缰,猜對了就是他亏掀。
    private Executor callbackExecutor;
    //我暫時理解為懶加載的意思,如果為true則把所有的methond都進(jìn)行反射运准,如果為false則用到哪個method再去反射幌氮。
    private boolean validateEagerly;
}
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();
    }

    // 這里會默認(rèn)添加一個適配器该互,這才能讓API中的load方法返回Call<Rep>
    List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
    //即加入ExecutorCallAdapterFactory
    adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

    // 沒有給默認(rèn)解析器,所以要自己定義一個解析器
    List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

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

adapterFactories:這個東西舉個栗子吧韭畸,由于adapterFactories中會默認(rèn)添加ExecutorCallAdapterFactory宇智,所以我們看到API中的方法返回Call是默認(rèn)可以解析的。如果接觸過RxJava的同學(xué)應(yīng)該知道胰丁,為了讓API中的方法能夠返回Observable随橘,則需要添加一個自定義Retrofit Adapters,而RxJavaCallAdapterFactory就是能夠讓API返回Observable的具體實現(xiàn)机蔗。所以你想自定義API中函數(shù)的返回類型,就需要添加自定義Adapter甘萧,而常見的Adapter在GayHub上都給有給出萝嘁。
converterFactories:還是很好理解的,一般的網(wǎng)絡(luò)請求后返回的body都是String形式的扬卷,那么怎么把String形式轉(zhuǎn)換成具體的entity對象呢牙言?就是他來實現(xiàn)的。你可以用Gson怪得、jackson來解析返回的數(shù)據(jù)咱枉,只要你配置了真確的converter就可以啦。retrofit-converters徒恋,而默認(rèn)中converterFactories為空的沒有具體的實現(xiàn)蚕断,所以在初始化Retrofit的時候需要添加例如:addConverterFactory(GsonConverterFactory.create())的converter。
這種可插拔式的框架因谎,可以靈活的適配項目之前已經(jīng)使用的一些框架基括。如果retrofit只支持fastjson解析,而之前項目都用Gson解析的财岔,那你會不會把之前的Gson都用fastjson替換(不然有潔癖的我是無法容忍的)风皿?這就是retrofit可插拔的優(yōu)點。

現(xiàn)在創(chuàng)建API的實例,通過Retrofit.create():

public <T> T create(final Class<T> service) {
    //確保API是一個接口匠璧,且該接口沒有繼承其他接口
    Utils.validateServiceInterface(service);
    //看看是不是懶加載桐款,如果true則會把API里面的所有method都進(jìn)行一次性緩存。
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    //動態(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);
            }
            //android平臺返回的是false夷恍,所以不管它
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            //API中每個方法都會生成對應(yīng)的一個ServiceMethod魔眨,解析該方法對應(yīng)的Annotation信息。
            ServiceMethod serviceMethod = loadServiceMethod(method);
            //他就是okhttp3.Call的一個包裝類酿雪,真正的網(wǎng)絡(luò)請求在這里面進(jìn)行遏暴,且是通過okhttp3.Call進(jìn)行的。
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            //返回該API中method的返回類型指黎,例如Call<Rep>
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

adapt()默認(rèn)返回的是ExecutorCallbackCall朋凉,他就是Call子類,所以你也可以直接把它理解為API函數(shù)的返回類型醋安,例如load的返回值Call<Rep>杂彭,沒有任何問題。
ServiceMethod咋一眼看去不知道什么鬼東西吓揪,其實他的存在就是:把API中的每個method方法解析成一個ServiceMethod對象亲怠,解析的內(nèi)容就是該method對應(yīng)的注解信息,把這些信息存儲在對應(yīng)的ServiceMethod對象中進(jìn)行緩存等待網(wǎng)絡(luò)請求使用柠辞。

ServiceMethod loadServiceMethod(Method method) {
    ServiceMethod result;
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = new ServiceMethod.Builder(this, method).build();
        //緩存团秽,不多說,總不可能在多次調(diào)用API方法的時候叭首,每次都使用反射進(jìn)行解析吧习勤。
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

下面就要開始解析ServiceMethod.Builder()方法,按照慣例只寫屬性和builder方法放棒。這樣姻报,一個method中所有信息都被保存在ServiceMethod當(dāng)中,后續(xù)的網(wǎng)絡(luò)請求只需要獲取ServiceMethod當(dāng)中的數(shù)據(jù)即可间螟。

static final class Builder<T> {
    //需要介紹嗎吴旋,就是上面loadServiceMethod傳入的this
    final Retrofit retrofit;
    //動過動態(tài)代理得到的method,后續(xù)要詳細(xì)的解析該method
    final Method method;
    //作用于該method的所有注解厢破,比如API方法中的GET
    final Annotation[] methodAnnotations;
    //method方法中參數(shù)對應(yīng)的注解荣瑟,一個參數(shù)可以對應(yīng)多個注解
    final Annotation[][] parameterAnnotationsArray;
    //method中參數(shù)對應(yīng)的真實類型列表
    final Type[] parameterTypes;
    //method返回類型,就是你看到的Call<Rep>摩泪。
    Type responseType;
    //這一堆boolean自行腦補吧
    boolean gotField;
    boolean gotPart;
    boolean gotBody;
    boolean gotPath;
    boolean gotQuery;
    boolean gotUrl;
    String httpMethod;
    boolean hasBody;
    boolean isFormEncoded;
    boolean isMultipart;
    String relativeUrl;
    Headers headers;
    MediaType contentType;
    Set<String> relativeUrlParamNames;
    ParameterHandler<?>[] parameterHandlers;
    Converter<ResponseBody, T> responseConverter;
    CallAdapter<?> callAdapter;
    
    public ServiceMethod build() {
      //獲取CallAdapter笆焰,可以通過該函數(shù)知道,最終會調(diào)用retrofit.callAdapter(returnType, annotations);
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      //最終調(diào)用retrofit.responseBodyConverter(responseType, annotations);
      responseConverter = createResponseConverter();
      //猜都不用猜见坑,肯定是解析retrofit2.http下面的注解嚷掠,解析完之后上面的一堆boolean值差不多都賦值好咯捏检。
      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);
        }
        //解析對應(yīng)參數(shù)和該參數(shù)的注解列表。
        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
        
        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }
      return new ServiceMethod<>(this);
    }
}

通過對上述對Method的解析不皆,得到了一一對應(yīng)的ServiceMethod對象贯城,最后通過serviceMethod.callAdapter.adapt(okHttpCall)返回一個API中函數(shù)的返回類型。由于系統(tǒng)默認(rèn)了使用ExecutorCallAdapterFactory霹娄,可以對比DefaultCallAdapterFactory能犯,他們的作用是一樣的,只是網(wǎng)絡(luò)請求返回的一個回調(diào)在主線程犬耻,一個回調(diào)在子線程:

final class DefaultCallAdapterFactory extends CallAdapter.Factory {
  static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();

  @Override
  public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    //這里的returnType是API中函數(shù)的返回類型踩晶,比如Call<Rep>
    //responseType則是Rep,就是returnType對應(yīng)的泛型類型枕磁。
    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public <R> Call<R> adapt(Call<R> call) {
        //看看渡蜻,直接把call返回了,這就是為什么可以處理API中函數(shù)是Call返回類型的原因透典。
        return call;
      }
    };
  }
}

好了晴楔,到這里Call<Rep> call = retrofit.create(API.class).load()我們就得到了Call,接下去就是enqueue了峭咒,這個過程有沒有很像Okhttp的enqueue税弃,知識點有沒有!前面我們已經(jīng)知道了這里對應(yīng)的Call就是OkhttpCall(不清楚的直接在動態(tài)代理里面找)凑队,所以要enqueue就去找OkhttpCall:

@Override public void enqueue(final Callback<T> callback) {
    //這是啥東西则果,哈哈,就是要去真真網(wǎng)絡(luò)請求的Call漩氨,不是retrofit的call西壮。
    //如果還是混淆了這兩個call,那么只能再多看看Okhttp是怎么網(wǎng)絡(luò)請求的叫惊。
    okhttp3.Call call;
    Throwable failure;

    //禁止同一個call入列兩次
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
          //從該函數(shù)的命名就可以看到他的意思款青,創(chuàng)建真實的Call,即Okhttp的Call
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          failure = creationFailure = t;
        }
      }
    }

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

    if (canceled) {
      call.cancel();
    }
    //真真的網(wǎng)絡(luò)請求來了霍狰,這一塊網(wǎng)絡(luò)請求就是Okhttp真真的網(wǎng)絡(luò)請求抡草,看到這你要還不懂的話,怪我咯蔗坯。
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
        Response<T> response;
        try {
        //通過真實返回的rawResponse康震,解析得到retrofit的response
          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();
        }
      }

      private void callFailure(Throwable e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
      //把成功的結(jié)果傳遞給回調(diào),其中response中包好了解析好的對象宾濒。    
      private void callSuccess(Response<T> response) {
        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    });
  }
  //把okhttp的返回rawResponse轉(zhuǎn)換成retrofit的Response腿短,并把解析內(nèi)容放入到Response當(dāng)中。
  Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();

    //該rawResponse只解析返回內(nèi)容的頭部,可以了解NoContentResponseBody讀取body拋異常了
    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) {
      return Response.success(null, rawResponse);
    }
    //異常捕獲response橘忱?不了解具體的用處赴魁。。鹦付。
    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
      //這個地方厲害了尚粘,就是通過convert來轉(zhuǎn)換成具體的對象择卦,比如用GsonConverterFactory.create()來把String轉(zhuǎn)換成具體對象敲长。自行看代碼,其實就一句話秉继。
      T body = serviceMethod.toResponse(catchingBody);
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      catchingBody.throwIfCaught();
      throw e;
    }
  }

到這里祈噪,我們就把解析好的T body返回給回調(diào)接口Callback里面去了,現(xiàn)在我們就可以在onResponse里面通過Response.body()獲取到解析以后的真正對象咯尚辑。
好了基本上能說的都說了辑鲤,應(yīng)該能夠差不多了解了Retrofit的使用流程了。

Utils

Retrofit中有一個非常重要的工具類Utils杠茬,里面包含了所有的關(guān)于Type的反射方法月褥,比如通過Call<Rep>得到Rep的Type等等。
Type是和泛型相關(guān)的接口瓢喉,具體分為4種類型:

  • ParameterizedType:具體的泛型類型宁赤,比如ArrayList<String>中,具體泛型就是String栓票。
  • TypeVariable:泛型變量决左,在泛型類里面使用該泛型變量,這時候該變量就是TypeVariable走贪。
  • GenericArrayType:泛型數(shù)組佛猛,String[],懂坠狡?
  • WildcardType:通配符泛型继找,比如ArrayList<? extends Number>。

不想講了逃沿,自己參考:Java中的Type詳解

四.總結(jié)

本文通過Okhttp的網(wǎng)絡(luò)請求為主線婴渡,然后把Retrofit是如何包裝Okhttp的過程進(jìn)行了分析,到這里你應(yīng)該有能力去修改Retrofit去適應(yīng)你自己app里面的業(yè)務(wù)功能了吧感挥。
最后再把參考的幾篇文章進(jìn)行羅列下缩搅,歡迎提出問題,我說雞蛋你說要:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末硼瓣,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌堂鲤,老刑警劉巖亿傅,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瘟栖,居然都是意外死亡葵擎,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進(jìn)店門半哟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來酬滤,“玉大人,你說我怎么就攤上這事寓涨《⒋” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵戒良,是天一觀的道長体捏。 經(jīng)常有香客問我,道長糯崎,這世上最難降的妖魔是什么几缭? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮沃呢,結(jié)果婚禮上年栓,老公的妹妹穿的比我還像新娘。我一直安慰自己樟插,他們只是感情好韵洋,可當(dāng)我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著黄锤,像睡著了一般搪缨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鸵熟,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天副编,我揣著相機與錄音,去河邊找鬼流强。 笑死痹届,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的打月。 我是一名探鬼主播队腐,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼奏篙!你這毒婦竟也來了柴淘?” 一聲冷哼從身側(cè)響起迫淹,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎为严,沒想到半個月后敛熬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡第股,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年应民,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夕吻。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡诲锹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出梭冠,到底是詐尸還是另有隱情辕狰,我是刑警寧澤,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布控漠,位于F島的核電站,受9級特大地震影響悬钳,放射性物質(zhì)發(fā)生泄漏盐捷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一默勾、第九天 我趴在偏房一處隱蔽的房頂上張望碉渡。 院中可真熱鬧,春花似錦母剥、人聲如沸滞诺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽习霹。三九已至,卻和暖如春炫隶,著一層夾襖步出監(jiān)牢的瞬間淋叶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工伪阶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留煞檩,地道東北人。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓栅贴,卻偏偏與公主長得像斟湃,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子檐薯,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,955評論 2 355

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