Retrofit配合RxJava2和Gson简单使用

Retrofit源码学习之一
Retrofit源码学习之二

本篇文章分析一下Retrofit结合RxJava2和Gson的使用的时候,CallAdapterFactory和ConverterFactory是如何起作用的。

使用k780api,获取现在的天气信息。

完整的请求路径
http://api.k780.com:88/?app=weather.today&weaid=1&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json&appkey=10003

我们请求的格式是json,返回的数据结构

{
  "success": "1",
  "result": {
    "weaid": "1",
    "days": "2018-09-13",
    "week": "星期四",
    "cityno": "beijing",
    "citynm": "北京",
    "cityid": "101010100",
    "temperature": "29℃/20℃",
    "temperature_curr": "22℃",
    "humidity": "82%",
    "aqi": "93",
    "weather": "多云",
    "weather_curr": "阴",
    "weather_icon": "http://api.k780.com/upload/weather/d/2.gif",
    "weather_icon1": "",
    "wind": "风",
    "winp": "0级",
    "temp_high": "29",
    "temp_low": "20",
    "temp_curr": "22",
    "humi_high": "0",
    "humi_low": "0",
    "weatid": "3",
    "weatid1": "",
    "windid": "423",
    "winpid": "207",
    "weather_iconid": "2"
  }
}

可以使用GsonFormat生成对应的Java文件。

定义请求方法

public interface API {
      @FormUrlEncoded
      @POST("/")
      Observable<NowWeatherBean> getNowWeather(@FieldMap Map<String, Object> map);
}

使用方式

private static final String BASE_URL = "http://api.k780.com";

 Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                //注释1处,添加CallAdapterFactory
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create()
                //注释2处,添加ConverterFactory
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        //注释3处,创建API的动态代理
        API api = retrofit.create(API.class);

        HashMap<String, Object> map = new HashMap<>();
        map.put("app", "weather.today");
        map.put("weaid", 1);
        map.put("appkey", 10003);
        map.put("sign", "b59bc3ef6191eb9f747dd4e83c99f2a4");
        map.put("format", "json");
        api.getNowWeather(map)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<NowWeatherBean>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(NowWeatherBean bean) {
                        Log.e(TAG, "onNext: " + bean.getResult().toString());
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.e(TAG, "onError: " + e.getMessage());
                    }

                    @Override
                    public void onComplete() {

                    }
                });

注释1处,添加了一个CallAdapter.Factory对象

RxJava2CallAdapterFactory的create方法

public static RxJava2CallAdapterFactory create() {
      //注意传入的参数
      return new RxJava2CallAdapterFactory(null, false);
}

RxJava2CallAdapterFactory的构造函数

private RxJava2CallAdapterFactory(Scheduler scheduler, boolean isAsync) {
    this.scheduler = scheduler;
    this.isAsync = isAsync;
}

在注释2处,添加了一个Converter.Factory对象。

GsonConverterFactory的create方法

public static GsonConverterFactory create() {
    return create(new Gson());
  }

public static GsonConverterFactory create(Gson gson) {
    if (gson == null) throw new NullPointerException("gson == null");
    return new GsonConverterFactory(gson);
  }

GsonConverterFactory的构造函数

private GsonConverterFactory(Gson gson) {
    this.gson = gson;
  }

注释3处,调用了Retrofit的create方法,创建API的动态代理。

Retrofit的create方法

public <T> T create(final Class<T> service) {
   
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
            //...
            //注释1处
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            //注释2处
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            //注释3处,将OkHttpCall 适配成我们想要的请求类型
            return serviceMethod.adapt(okHttpCall);
          }
        });
  }
public ServiceMethod build() {
      //创建CallAdapter
      callAdapter = createCallAdapter();
      //创建Converter<ResponseBody, T> responseConverter
      responseConverter = createResponseConverter();
      //解析方法注解
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
     
      //方法参数注解数组的长度,注意parameterAnnotationsArray是一个二维数组
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      //遍历解析方法的每个参数的注解
      for (int p = 0; p < parameterCount; p++) {
        //获取对应的参数类型
        Type parameterType = parameterTypes[p];
        //获取每个参数的注解数组(每个参数可以有多个注解,但是只能有一个Retrofit注解)
        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
        //为parameterHandlers赋值
        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }
      //...
      return new ServiceMethod<>(this);
}

注释1处,构造ServiceMethod对象

  • 创建CallAdapter
  • 创建Converter<ResponseBody, T> responseConverter
  • 解析方法注解 parseMethodAnnotation
  • 解析方法参数注解,创建parameterHandlers
ServiceMethod创建CallAdapter

构建ServiceMethod对象的时候,会遍历Retrofit的callAdapterFactories,根据(我们在接口中定义的)方法的返回类型和注解,返回合适的CallAdapter。现在我们的方法返回类型是Observable<NowWeatherBean>。在Android平台下,默认的CallAdapter只能处理retrofit2.Call<T>这种返回类型,所以要从我们添加的RxJava2CallAdapterFactory中获取CallAdapter。

看一下RxJava2CallAdapterFactory这个类的get方法

public final class RxJava2CallAdapterFactory extends CallAdapter.Factory {
 
  //获取CallAdapter
  @Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, 
  Retrofit retrofit) {
    //returnType是泛型类型Observable<NowWeatherBean>
    //rawType是Observable
    Class<?> rawType = getRawType(returnType);

   //...

    boolean isResult = false;
    boolean isBody = false;
    Type responseType;
    //...
    //获取的observableType是NowWeatherBean
    Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);
    Class<?> rawObservableType = getRawType(observableType);
    if (rawObservableType == Response.class) {//条件不满足
     
    } else if (rawObservableType == Result.class) {//条件不满足
     
    } else {
       //最终responseType就是NowWeatherBean
        responseType = observableType;
        isBody = true;
    }
    //返回一个RxJava2CallAdapter对象
    return new RxJava2CallAdapter(responseType, scheduler, isAsync, isResult,
        isBody, isFlowable, isSingle, isMaybe, false);
    }
}

方法返回了一个RxJava2CallAdapter对象。

final class RxJava2CallAdapter<R> implements CallAdapter<R, Object> {
  
  //传入的参数注意一下
  RxJava2CallAdapter(Type responseType, @Nullable Scheduler scheduler, 
    boolean isAsync, boolean isResult, boolean isBody, boolean isFlowable, 
    boolean isSingle, boolean isMaybe, boolean isCompletable) {
    this.responseType = responseType;//NowWeatherBean
    this.scheduler = scheduler;//null
    this.isAsync = isAsync;//false
    this.isResult = isResult;//false
    this.isBody = isBody;//true
    this.isFlowable = isFlowable;//false
    this.isSingle = isSingle;//false
    this.isMaybe = isMaybe;//false
    this.isCompletable = isCompletable;//false
  }

  @Override public Type responseType() {
    //注释1处,NowWeatherBean
    return responseType;
  }

  @Override public Object adapt(Call<R> call) {
    //传入的isAsync是false,responseObservable取值是CallExecuteObservable
    Observable<Response<R>> responseObservable = isAsync
        ? new CallEnqueueObservable<>(call)
        : new CallExecuteObservable<>(call);

    Observable<?> observable;
    if (isResult) {
      observable = new ResultObservable<>(responseObservable);
    } else if (isBody) {//传入的isBody是true
      observable = new BodyObservable<>(responseObservable);
    } else {
      observable = responseObservable;
    }
    //这里还能指定线程
    if (scheduler != null) {
      observable = observable.subscribeOn(scheduler);
    }

    //注释2处,使用一个CallExecuteObservable对象构建一个BodyObservable对象
    return observable;
  }
}

在注释1处,RxJava2CallAdapter的responseType方法返回的类型就是NowWeatherBean。
在注释2处,RxJava2CallAdapter的adapt方法会把一个OkHttpCall对象转换成了一个BodyObservable对象。

ServiceMethod创建ResponseBodyConverter

构建ServiceMethod对象的时候,会遍历Retrofit的converterFactories,根据callAdapter的responseType方法返回的类型信息,返回合适的responseBodyConverter。现在responseType是NowWeatherBean,因为在Android平台下,默认的responseBodyConverter只能处理okhttp3.ResponseBody类型和Void类型。所以要从我们添加的GsonConverterFactory中获取responseBodyConverter。

public final class GsonConverterFactory extends Converter.Factory {

  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type,
 Annotation[] annotations,Retrofit retrofit) {
    //我们传入的Type是NowWeatherBean
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    //返回GsonResponseBodyConverter
    return new GsonResponseBodyConverter<>(gson, adapter);
  }

  @Override
  public Converter<?, RequestBody> requestBodyConverter(Type type,Annotation[]
  parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonRequestBodyConverter<>(gson, adapter);
  }
}

GsonConverterFactory的responseBodyConverter方法返回了一个GsonResponseBodyConverter对象。

顺便提一句,在这个例子中,GsonConverterFactory的requestBodyConverter方法没有被调用。GsonConverterFactory的requestBodyConverter方法返回的GsonRequestBodyConverter对象,用来把okhttp3.RequestBody 类型的请求体转化成合适的类型。我们先不去关注这问题,继续往下看。

GsonResponseBodyConverter类

final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
  private final Gson gson;
  private final TypeAdapter<T> adapter;

  GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
    this.gson = gson;
    this.adapter = adapter;
  }

  @Override public T convert(ResponseBody value) throws IOException {
    //从ResponseBody中获取字符流,然后转化成我们想要的对象,这里就是NowWeatherBean对象。
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      T result = adapter.read(jsonReader);
      if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
        throw new JsonIOException("JSON document was not fully consumed.");
      }
      return result;
    } finally {
      value.close();
    }
  }
}

GsonResponseBodyConverter的convert方法会把okhttp3.ResponseBody类型的响应体,转化成我们希望的数据类型,在这个例子中就是NowWeatherBean。

ServiceMethod解析方法注解 parseMethodAnnotation

    //解析方法注解
    for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
   }

ServiceMethod.Builder的parseMethodAnnotation方法

private void parseMethodAnnotation(Annotation annotation) {
     //...
    if (annotation instanceof POST) {
        parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
      } else if (annotation instanceof FormUrlEncoded) {
        //...
        isFormEncoded = true;
      }
    }
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
      //...
      this.httpMethod = httpMethod;
      this.hasBody = hasBody;
      //注解值为空,直接返回,我们注解中传入的是 "/"
      if (value.isEmpty()) {
        return;
      }
      //...
      //获取相对路径,
      this.relativeUrl = value;
     //...
    }
ServiceMethod解析方法参数注解,创建parameterHandlers

我们的方法参数注解只有一个@FieldMap

      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
        //parameterType是Map<String, Object>
        Type parameterType = parameterTypes[p];
        //...
        //parameterAnnotations是[@FieldMap]
        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
        //...
        
        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }

ServiceMethod.Builder的parseParameter方法

private ParameterHandler<?> parseParameter(int p, Type parameterType, Annotation[] annotations) {
      ParameterHandler<?> result = null;
      //遍历解析Retrofit参数注解数组
      for (Annotation annotation : annotations) {
          //调用parseParameterAnnotation方法
          ParameterHandler<?> annotationAction = parseParameterAnnotation(
            p, parameterType, annotations, annotation);

          if (annotationAction == null) {
              continue;
          }
   
          result = annotationAction;
      }
      //...
      return result;
 }

ServiceMethod.Builder的parseParameterAnnotation方法

private ParameterHandler<?> parseParameterAnnotation(
      int p, Type type, Annotation[] annotations, Annotation annotation) {
      //...
      else if (annotation instanceof FieldMap) {
         //rawParameterType 类型是Map类型
         Class<?> rawParameterType = Utils.getRawType(type);
      //mapType是Map<String, Object>
      Type mapType = Utils.getSupertype(type, rawParameterType, Map.class);
        
      ParameterizedType parameterizedType = (ParameterizedType) mapType;
      //keyType是String
      Type keyType = Utils.getParameterUpperBound(0, parameterizedType);
     //valueType是Object
      Type valueType = Utils.getParameterUpperBound(1, parameterizedType);
      //注释1处,valueType是Object
      Converter<?, String> valueConverter =
          retrofit.stringConverter(valueType, annotations);

      gotField = true;
     //查找valueConverter,最后返回ParameterHandler.FieldMap对象
      return new ParameterHandler.FieldMap<>(valueConverter, ((FieldMap) annotation).encoded());

    } 
}

我们看一下上面方法的注释1处

//注释1处,valueType是Object
Converter<?, String> valueConverter = retrofit.stringConverter(valueType, annotations);


Retrofit的stringConverter方法

public <T> Converter<T, String> stringConverter(Type type, Annotation[] annotations) {
    
    for (int i = 0, count = converterFactories.size(); i < count; i++) {
        Converter<?, String> converter =
            converterFactories.get(i).stringConverter(type, annotations, this);
        if (converter != null) {
            return (Converter<T, String>) converter;
        }
    }
    // 没有匹配的Converter,求助于默认转换器,它只调用toString()。
    return (Converter<T, String>) BuiltInConverters.ToStringConverter.INSTANCE;
}

无论是Retrofit内置的BuiltInConverters,还是我们添加的GsonConverterFactory,这两个对象都没有重写Converter.Factory的stringConverter方法,所以它俩的stringConverter方法返回的都是null。所以Retrofit的stringConverter方法最终返回的是一个BuiltInConverters.ToStringConverter对象。

static final class ToStringConverter implements Converter<Object, String> {
    static final ToStringConverter INSTANCE = new ToStringConverter();

    @Override public String convert(Object value) {
        return value.toString();
    }
}

ToStringConverter的convert方法返回的Object的toString方法的返回值。

在这个例子中,ServiceMethod类创建的parameterHandlers 就是一个ParameterHandler.FieldMap实例。ParameterHandler.FieldMap类的apply方法内部就是把我们的map参数中的value值都转化成String,然后添加到RequestBuilder 中。

ParameterHandler.FieldMap类的apply方法精简版

@Override 
void apply(RequestBuilder builder, Map<String, T> value) throws IOException {
      //...
      for (Map.Entry<String, T> entry : value.entrySet()) {
        String entryKey = entry.getKey();
      
        T entryValue = entry.getValue();
        //valueConverter就是ToStringConverter对象
        String fieldEntry = valueConverter.convert(entryValue);
        //注释1处,加入RequestBuilder,以键值对的方式,构建我们的表单提交请求体
        builder.addFormField(entryKey, fieldEntry, encoded);
    }
}

注释1处,调用RequestBuilder的addFormField方法

void addFormField(String name, String value, boolean encoded) {
    if (encoded) {
        formBuilder.addEncoded(name, value);
    } else {
        //默认是不加密的
        formBuilder.add(name, value);
    }
}

调用FormBody.Builder的add方法

public Builder add(String name, String value) {
     private final List<String> names = new ArrayList<>();
     private final List<String> values = new ArrayList<>();

      names.add(HttpUrl.canonicalize(name, FORM_ENCODE_SET, false, false, true, true, charset));
      values.add(HttpUrl.canonicalize(value, FORM_ENCODE_SET, false, false, true, true, charset));
      return this;
}

可以看到内部就是以键值对的方式,构建我们的表单提交请求体。

2.构建OkHttpCall实例。

3. 返回最后经过serviceMethod转换后的对象。

return serviceMethod.adapt(okHttpCall);
T adapt(Call<R> call) {
    return callAdapter.adapt(call);
}

经过上面的分析,我们知道ServiceMethod的callAdapter就是RxJava2CallAdapter。RxJava2CallAdapter的adapt方法把一个OkHttpCall对象转换成了一个BodyObservable对象。

当我们订阅的时候,会调用BodyObservable的subscribeActual方法。

//方法中的参数observer就是我们的订阅者,方法内部把我们的observer包装成了一个BodyObserver对象
@Override protected void subscribeActual(Observer<? super T> observer) {
    upstream.subscribe(new BodyObserver<T>(observer));
  }

我们从传入BodyObservable的upstream是一个CallExecuteObservable对象

CallExecuteObservable类精简版

final class CallExecuteObservable<T> extends Observable<Response<T>> {
    private final Call<T> originalCall;

    CallExecuteObservable(Call<T> originalCall) {
         this.originalCall = originalCall;
    }

    @Override
    protected void subscribeActual(Observer<? super Response<T>> observer) {
        // Since Call is a one-shot type, clone it for each new observer.
        Call<T> call = originalCall.clone();
        //...
        //调用传入的OkHttpCall的execute方法,并调用传入的BodyObserver的onNext方法
         Response<T> response = call.execute();
          if (!disposable.isDisposed()) {
             //调用观察者BodyObserver的onNext方法
             observer.onNext(response);
         }
    }
}

OkHttpCall的execute方法精简版

public Response<T> execute() throws IOException {
      okhttp3.Call call;
      //注释1处,创建一个请求
      call = rawCall = createRawCall();
      //注释2处,获取执行结果,并将结果转换成期望的类型
      return parseResponse(call.execute());
}

OkHttpCall的createRawCall方法精简版

private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = serviceMethod.toCall(args);
    return call;
 }

ServiceMethod的toCall方法

okhttp3.Call toCall(@Nullable Object... args) throws IOException {
    //注释1处,RequestBuilder 用来构建我们的请求
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl,
    headers, contentType, hasBody, isFormEncoded, isMultipart);
    //这里的handlers长度是1 ,里面是一个ParameterHandler.FieldMap对象
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
    
    //...
    
    for (int p = 0; p < argumentCount; p++) {
      handlers[p].apply(requestBuilder, args[p]);
    }
    callFactory是一个OkHttpClient实例
    return callFactory.newCall(requestBuilder.build());
  }

ServiceMethod的createRawCall()的过程,就是使用ServiceMethod解析方法注解获取的信息和ServiceMethod创建的parameterHandlers解析出来的方法参数信息构建一个okhttp3.Request实例,在这个例子中,我们的请求体是一个FormBody对象。然后使用OkHttpClient创建一个okhttp3.RealCall实例。

RequestBuilder的build方法

Request build() {
    HttpUrl url;
   
    url = baseUrl.resolve(relativeUrl);
    

    RequestBody body = this.body;
    if (body == null) {
      // 我们是表单提交,此条件满足
      if (formBuilder != null) {
        //返回的是一个okhttp3.FormBody对象
        body = formBuilder.build();
      } 
    }
    //返回一个okhttp3.Request对象
    return requestBuilder
        .url(url)
        .method(method, body)
        .build();
}

现在我们的请求准备好了,开始执行,并转换执行结果了。

//注释2处,获取执行结果,并将结果转换成期望的类型
return parseResponse(call.execute());

执行的过程我们就不看了。我们看一下处理响应结果的过程。

OkHttpCall的parseResponse方法

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

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

    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    //调用ServiceMethod的toResponse方法
    T body = serviceMethod.toResponse(catchingBody);
      return Response.success(body, rawResponse);
    
  }

ServiceMethod的toResponse方法

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

在这个例子中,ServiceMethod的responseConverter就是GsonResponseBodyConverter类的实例,GsonResponseBodyConverter的 convert方法从ResponseBody中获取字符流,然后转化成我们想要的对象,这里就是NowWeatherBean对象。所以OkHttpCall的parseResponse方法最后返回的是一个响应体为NowWeatherBean类型的的retrofit2.Response对象。

我们回到CallExecuteObservable的subscribeActual()方法

@Override
protected void subscribeActual(Observer<? super Response<T>> observer) {
    // Since Call is a one-shot type, clone it for each new observer.
    Call<T> call = originalCall.clone();
    //...
    //这里的response 就是Response<NowWeatherBean>类型的对象了。
     Response<T> response = call.execute();
      if (!disposable.isDisposed()) {
        //调用observer.onNext()方法
        observer.onNext(response);
      }
  }

然后我们回到BodyObservable的subscribeActual()方法

//方法中的参数observer就是我们的订阅者,方法内部把我们的observer包装成了一个BodyObserver对象
@Override
protected void subscribeActual(Observer<? super T> observer) {
    upstream.subscribe(new BodyObserver<T>(observer));
}

BodyObserver是BodyObservable的内部类,BodyObserver就是把我们真正的observer做了一层包装而已。BodyObserver这个名字很好,一看就知道,只关注响应体。看一下BodyObserver的精简版。

private static class BodyObserver<R> implements Observer<Response<R>> {

    private final Observer<? super R> observer;
  
    //参数中的observer使我们真正的订阅对象
    BodyObserver(Observer<? super R> observer) {
        this.observer = observer;
    }

    @Override
    public void onNext(Response<R> response) {
        //最终调用我们的observer的onNext()方法,response.body就是NowWeatherBean类的实例。
        observer.onNext(response.body());
      } 
    }
}

结束。如果以后有时间会画一个流程图出来,不过感觉可能不会画了,哈哈。本篇文章以一个例子,结合前两篇,梳理了Retrofit的通常使用的场景。Retrofit的学习暂时告一段落。在后续的学习和研究中,会不断改正文章中的错误,完善理解不到位的地方。后续的打算是看看《图解HTTP》这本书,和《RxJava2.x实战》这两本书,并学习RxJava2的相关源码。

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