Retrofit源码分析

本篇为个人理解心得 多有不详细 还请移步:
https://www.jianshu.com/p/0c055ad46b6c

从retrofit的简单使用开始
retrofit使用步骤:
1.创建Retrofit实例
2.创建 网络请求接口实例 并 配置网络请求参数
3.发送网络请求
4.处理服务器返回的数据

一.创建retrofit实例

        Retrofit retrofit = new Retrofit//第一步
                .Builder()//第二步
                .baseUrl("http://apis.juhe.cn/")//第三步
                .addConverterFactory(GsonConverterFactory.create())//第四步
                .build();//第五步

这里可以看到:Retrofit实例是使用建造者模式通过Builder类进行创建的
分为五个步骤逐步分析:

第一步 new Retrofit

//Retrofit类
public final class Retrofit {

  private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();
//网络请求配置对象 存储网络请求相关配置(请求方法,数据转换器,baseurl等)
  final okhttp3.Call.Factory callFactory;
//网络请求器工厂:用于生产网络请求器 默认使用okhttp
  final HttpUrl baseUrl;//服务器地址baseurl
  final List<Converter.Factory> converterFactories;
//数据转换器工厂集合 (允许多个不同数据转换器工厂放入其中)
//数据转换器工厂用于生产数据转换器 converter
  final List<CallAdapter.Factory> callAdapterFactories;
  // 网络请求适配器工厂的集合(允许多个不同网络请求适配器工厂放入其中)
  // 网络请求适配器工厂作用:生产网络请求适配器(CallAdapter)
//将默认的网络请求器(OkhttpCall)转化为适合不同平台来调用的网络请求执行器形式
  final @Nullable Executor callbackExecutor;
  final boolean validateEagerly;
//retrofit构造函数
  Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
      List<Converter.Factory> converterFactories, List<CallAdapter.Factory> callAdapterFactories,
      @Nullable Executor callbackExecutor, boolean validateEagerly) {
    this.callFactory = callFactory;
    this.baseUrl = baseUrl;
    this.converterFactories = converterFactories; // Copy+unmodifiable at call site.
    this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.
    this.callbackExecutor = callbackExecutor;
    this.validateEagerly = validateEagerly;
//省略的代码
    ......
  }

建立好Retrofit对象的一个标准:

  • 包含所有网络请求信息的对象 serviceMethod
  • 网络请求的url地址 baseUrl
  • 网络请求工厂 callFactory
  • 网络请求适配器工厂的集合 callAdapterFactories
  • 数据转换器工厂的集合 converterFactories
  • 回调方法执行器 callbackExecutor

第二步 .Builder()

Retrofit建造者

  public static final class Builder {
//Builder类的成员变量与Retrofit类的成员变量是对应的
//Retrofit类的成员变量基本上是通过Builder类进行配置
    private final Platform platform;
    private @Nullable okhttp3.Call.Factory callFactory;
    private HttpUrl baseUrl;
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
    private @Nullable Executor callbackExecutor;
    private boolean validateEagerly;

    Builder(Platform platform) {//有参构造方法
      this.platform = platform;
    }

//采用Builder的无参构造方法
    public Builder() {
//this调用自己已有的上边的有参构造方法
//通过调用Platform.get()传入了Platform对象
      this(Platform.get());

    }
//省略的代码
......
}

以上可以看到builder的无参构造方法中调用了有参构造方法
Builder(Platform platform)
并通过Platform.get()传入需要的Platform对象
紧接着看Platform.get()代码:

class Platform {
//findPlatform()赋值到静态变量PLATFORM 
  private static final Platform PLATFORM = findPlatform();
  static Platform get() {
// 返回静态变量PLATFORM,即findPlatform()
    return PLATFORM;
  }
//省略的代码
......
}

以上可以看到在Platform中,首先通过findPlatform()赋值静态变量PLATFORM,然后get()方法返回PLATFORM
接下来看看findPlatform()方法

  private static Platform findPlatform() {
    try {
//查找并加载指定类android.os.Build
      Class.forName("android.os.Build");
//// 如果是Android平台,就创建并返回一个Android对象
      if (Build.VERSION.SDK_INT != 0) {
        return new Android();
      }
    } catch (ClassNotFoundException ignored) {
    }
//这里添加对java平台的支持 同上
    try {
      Class.forName("java.util.Optional");
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
// 从上面看出此版本Retrofit支持2个平台:Android平台、Java平台
// 最后返回一个Platform对象给Builder的有参构造方法public Builder(Platform platform) 
    return new Platform();
  }

以上我们可以看到Builder指定了运行平台,这里主要看指定了Android平台后的操作,通过return new Android()创建并返回了一个Android对象
接下来看看Android类的源代码:

  static class Android extends Platform {

ExecutorCallAdapterFactory(默认)、GuavaCallAdapterFactory、Java8CallAdapterFactory、RxJavaCallAdapterFactory
    @Override public Executor defaultCallbackExecutor() {
      // 返回一个默认的回调方法执行器
      // 该执行器作用:切换线程(子->>主线程),并在主线程(UI线程)中执行回调方法
      return new MainThreadExecutor();
    }

    // 创建默认的网络请求适配器工厂
    // 该默认工厂生产的 adapter 会使得Call在异步调用时在指定的 Executor 上执行回调
    // 在Retrofit中提供了四种CallAdapterFactory: 
    @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }


    static class MainThreadExecutor implements Executor {
// 获取与Android 主线程绑定的Handler 
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override public void execute(Runnable r) {
        // 该Handler是上面获取的与Android 主线程绑定的Handler 
        // 在UI线程进行对网络请求返回数据处理等操作。
        handler.post(r);
      }
    }
  }

总结一下,Builder设置了默认的

  • 平台类型对象:Android
  • 网络请求适配器工厂:CallAdapterFactory
  • 数据转换器工厂: converterFactory
  • 回调执行器:callbackExecutor
这里Builder只是设置默认值,并没有真正配置到具体的Retrofit类的成员变量中

第三步.baseUrl("http://apis.juhe.cn/")

配置baseUrl
baseUrl(String baseUrl)源代码

    public Builder baseUrl(String baseUrl) {
//首先检查是否为空
      checkNotNull(baseUrl, "baseUrl == null");
// 把String类型的url参数转化为适合OKhttp的HttpUrl类型
      HttpUrl httpUrl = HttpUrl.parse(baseUrl);
      if (httpUrl == null) {
        throw new IllegalArgumentException("Illegal URL: " + baseUrl);
      }
//最终返回重载的baseUrl(HttpUrl baseUrl)参数类型为httpUrl
      return baseUrl(httpUrl);
    }

以上我们可以看到baseUrl(String baseUrl)对传入的String型的baseUrl进行了非空判断,并且将baseUrl类型转换到适合OKhttp的HttpUrl类型,并返回重载的baseUrl(HttpUrl baseUrl)
下面看baseUrl(HttpUrl baseUrl)源码

    public Builder baseUrl(HttpUrl baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
//把URL参数分割成几个路径碎片
      List<String> pathSegments = baseUrl.pathSegments();
      if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
// 检测最后一个碎片来检查URL参数是不是以"/"结尾
// 不是就抛出异常    
        throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
      }
      this.baseUrl = baseUrl;
      return this;
    }

总结:baseUrl()用于配置Retrofit类的网络请求url地址,其内部对baseUrl进行了简单处理和判断,规定了baseUrl必须以 / 结尾

第四步.addConverterFactory(GsonConverterFactory.create())

由里到外,先看GsonConverterFactory.creat()源码

public final class GsonConverterFactory extends Converter.Factory {
//首先是调用了这个无参的create()
  public static GsonConverterFactory create() {
// 创建一个Gson对象 传给create(Gson gson) 并返回
    return create(new Gson());
  }

  @SuppressWarnings("ConstantConditions") // Guarding public API nullability.
  public static GsonConverterFactory create(Gson gson) {
    if (gson == null) throw new NullPointerException("gson == null");
// 创建了一个含有Gson对象实例的GsonConverterFactory 传给构造函数GsonConverterFactory(Gson gson) 并返回
    return new GsonConverterFactory(gson);
  }

  private final Gson gson;
//最终返回的是以下构造函数生成的GsonConverterFactory对象
  private GsonConverterFactory(Gson gson) {
    this.gson = gson;
  }

以上我们可以看到GsonConverterFactory.creat()是创建了一个含有Gson对象实例的GsonConverterFactory,并返回给addConverterFactory()

接下来看addConverterFactory()

// 将上面创建的GsonConverterFactory放入到 converterFactories数组
// 在第二步放入一个内置的数据转换器工厂BuiltInConverters()后又加入了一个GsonConverterFactory
    public Builder addConverterFactory(Converter.Factory factory) {
      converterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }

总结:第四部创建了一个含有Gson对象实例的GsonConverterFactory并放入到数据转换器工厂converterFactories
在数据转换器工厂集合中加入GsonConverterFactory是用于序列化 Java 对象为 JSON 字符串,或反序列化 JSON 字符串成 Java 对象。
使用到了谷歌的Gson框架来进行数据的转换,当服务器返回的数据是json格式的时候,可以通过这个框架来转换为实体对象。

我们配置的Retrofit默认使用Gson进行解析,若使用其他解析方式(XML或Protocobuf),也可通过自定义数据解析器来实现

注:Gson不要求本地实体类和返回的json所以数据字段完全匹配
实体类没有的字段但服务器中有返回,该字段无法被使用
实体类中有的字段但服务器没有返回,该字段为空
(此时直接调用没有返回的字段会出前空指针错误)

第五步.build()

看源码

    public Retrofit build() {
      //如果baseUrl 是空,抛出异常
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      //配置网络请求执行器(callFactory )
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
      // 如果没指定,则默认使用okhttp
      // 这里可以看到Retrofit默认使用okhttp进行网络请求
        callFactory = new OkHttpClient();
      }

      //配置回调方法执行器(callbackExecutor)
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
      // 如果没指定,则默认使用Platform检测环境时的默认callbackExecutor
      // 即Android默认的callbackExecutor
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      //配置网络请求适配器工厂(CallAdapterFactory)
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
//// 向该集合中添加了第二步中创建的CallAdapter.Factory请求适配器
      callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

//配置数据转换器工厂(converterFactory)
      List<Converter.Factory> converterFactories =
          new ArrayList<>(1 + this.converterFactories.size());
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      // 在第二步中已经添加了内置的数据转换器 BuiltInConverters()(添加到集合器的第一位)
      // 在第四步中又插入了一个Gson的转换器 GsonConverterFactory(添加到集合器的第二位)

     //1获取合适的网络请求适配器和数据转换器都是从adapterFactories和converterFactories集合的首位开始
     // 因此集合中的工厂位置越靠前就拥有越高的使用权重
 

     // 最终返回一个Retrofit的对象,并传入上述已经配置好的成员变量
      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }

总结:在最后一步中,通过前面步骤设置的变量,将Retrofit类的所有成员变量都配置完毕。

Retrofit使用建造者模式通过Builder类建立了一个Retrofit实例,具体创建细节是配置了:

  • 平台类型对象(Platform - Android)
  • 网络请求的url地址(baseUrl)
  • 网络请求工厂(callFactory)默认okhttpcall
  • 网络请求适配器工厂的集合(adapterFactories)
  • 数据转换器工厂的集合(converterFactories)
  • 回调方法执行器(callbackExecutor)

在创建Retrofit对象时,通过更多更灵活的方式去处理需求,如使用不同的Converter、使用不同的CallAdapter,这也就提供了使用RxJava来调用Retrofit的支持

二.创建网络请求接口的实例

还是从最简单的用法开始:
1.根据返回的数据定义接收数据的实体类
2.定义网络请求接口
3.创建接口实例
4.对发送请求的url进行封装生成最终的网络请求对象

2.1 简单使用

2.1.1:
  • 1.根据返回的数据定义接收数据的实体类
  • 2.定义网络请求接口
//第一步 接收数据的实体类
public class WeatherBean{
......
}
//第二步 定义网络请求接口
public interface APIService {
    // 注解GET:采用Get方法发送网络请求
    // Retrofit把网络请求的URL分成了2部分:1部分baseurl放在创建Retrofit对象时设置;另一部分在网络请求接口设置(即这里)
    // 如果接口里的URL是一个完整的网址,那么放在创建Retrofit对象时设置的部分可以不设置
    @GET("simpleWeather/query")
// 接受网络请求数据的方法
// 返回类型为Call<*>,*是解析得到的数据类型,即WeatherBean
//getWeather还包含两个参数 采用@Query注解加入 city(城市),key(聚合数据用户识别码)
    Call<WeatherBean> getWeather(@Query("city") String city, @Query("key") String key);
}

补充retrofit对url的拼接规则

  • 如果注解中提供的是完整的url,则将作为请求的url
  • 如果注解中提供的是不完整的url,且不以“/”开头,则请求的url为baseurl+注解提供的值
  • 如果注解中提供的是不完整的url,且以“/”开头,则请求的url为baseurl的主机部分+注解提供的值

retrofit网络请求参数注解用法:
https://www.jianshu.com/p/6f80fe0e85cc

2.1.2
  • 3.创建接口实例
  • 4.对发送请求的url进行封装生成最终的网络请求对象
//创建接口实例
        APIService apiService = retrofit.create(APIService.class);
//对发送请求的url进行封装生成最终的网络请求对象
//这里需要传入两个参数
        Call<WeatherBean> call = apiService.getWeather("成都", "***我的key***");

2.2

主要对 3,4进行源码分析

  • 3.创建接口实例
  • 4.对发送请求的url进行封装生成最终的网络请求对象
//创建接口实例
        APIService apiService = retrofit.create(APIService.class);
//对发送请求的url进行封装生成最终的网络请求对象
//这里需要传入两个参数
        Call<WeatherBean> call = apiService.getWeather("成都", "***我的key***");
2.2.1

第三步 源码分析APIService apiService = retrofit.create(APIService.class);
我们看retrofit.create(final Class<T> service)的源码:

  public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }

// 创建了网络请求接口的动态代理对象
//通过动态代理创建网络请求接口的实例 (并最终返回)
// 该动态代理是为了拿到网络请求接口实例上所有注解
    return (T) Proxy.newProxyInstance(
        service.getClassLoader(),  // 动态生成接口的实现类 
        new Class<?>[] { service },// 动态创建实例
        new InvocationHandler() {// 将代理类的实现交给 InvocationHandler类作为具体的实现

          private final Platform platform = Platform.get();
          // 在 InvocationHandler类的invoke()实现
          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            // 下面三行代码即是 invoke()的实现

//1.读取网络请求接口里的方法,并根据前面配置好的属性配置
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);

//2.根据配置好的serviceMethod对象创建okHttpCall对象 
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
// 3.调用OkHttp,并根据okHttpCall返回rxjava的Observe对象或者返回Call
            return serviceMethod.adapt(okHttpCall);
//以上代码中通过动态生成的代理类,调用interfaces接口的方法
//实际上是通过调用InvocationHandler对象的invoke()来完成指定的功能
          }
        });
  }

总结:
return (T) roxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler invocationHandler)通过代理模式中的动态代理模式,动态生成网络请求接口的代理类,并将代理类的实例创建交给InvocationHandler类 作为具体的实现,并最终返回一个动态代理对象。

2.2.2 第四步分析 Call<WeatherBean> call = apiService.getWeather("成都", "***我的key***");
  • apiService对象实际上是动态代理对象Proxy.newProxyInstance()(第三步中完成),并不是真正的网络请求接口创建的对象
  • apiService调用getWeather时会被动态代理对象Proxy.newProxyInstance()拦截,然后调用自身的InvocationHandler # invoke()最终返回一个OkHttpCall的call对象

三 发送网络请求

Rretrofit默认使用OkHttp,即OkHttpCall类,OkHttpCall提供了两种请求方式:

  • 异步请求OkHttpCall.enqueue()
  • 同步请求OkHttpCall.execute()

3.1 下边是异步请求的分析OkHttpCall.enqueue()

3.1.1 发送请求的步骤

1.对网络请求接口的方法中的每个参数利用对应ParameterHandler进行解析,再根据ServiceMethod对象创建一个OkHttp的Request对象
2.使用OkHttp的Request发送网络请求
3.对返回的数据使用之前设置的数据转换器(GsonConverterFactory)解析返回的数据,最终得到一个Response<T>对象
4.进行线程切换从而在主线程处理返回的数据结果 这里可以使用Rxjava

异步请求会将回调方法交给回调执行器在指定的线程中执行
3.1.2 最简单的使用实现

        call.enqueue(new Callback<WeatherBean>() {
            @Override
            public void onResponse(Call<WeatherBean> call, Response<WeatherBean> response) {
//请求成功后对返回的数据进行处理 更新ui等操作
                WeatherBean.ResultBean data = response.body().getResult();
                if(data!=null){textView.setText(data.getCity() + ":" + data.getRealtime().getInfo());}
                else {textView.setText(response.body().getReason());}
            }
            @Override
            public void onFailure(Call<WeatherBean> call, Throwable t) {
//请求失败显示失败的原因
                textView.setText(t.toString());
            }
        }

3.1.3 源码分析 call.enqueue()
ExecutorCallAdapterFactory.ExecutorCallbackCall.enqueue(final Callback<T> callback)

    @Override public void enqueue(final Callback<T> callback) {
//检查callback是否为空
      checkNotNull(callback, "callback == null");
// 使用静态代理 delegate进行异步请求
      delegate.enqueue(new Callback<T>() {
        @Override public void onResponse(Call<T> call, final Response<T> response) {
//线程切换,从而在主线程 更新ui显示结果
          callbackExecutor.execute(new Runnable() {
 // 最后Okhttp的异步请求结果返回到callbackExecutor
          // callbackExecutor.execute()通过Handler异步回调将结果传回到主线程进行处理(如显示在Activity等等),即进行了线程切换
            @Override public void run() {
              if (delegate.isCanceled()) {
                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);
            }
          });
        }
      });
    }


我们可以看到在以上源码中 call.enqueue() 通过OkHttpcall的静态代理delegate.enqueue来进行异步请求。
OkHttp异步请求结果返回到callbackExecutor,然后切换线程进行处理

3.1.4 现在来看OkHttpCall..enqueue()源码


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

    okhttp3.Call call;
    Throwable failure;
// 步骤1:创建OkHttp的Request对象,再封装成OkHttp.call
     // delegate代理在网络请求前的动作:创建OkHttp的Request对象,再封装成OkHttp.call
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
          // 创建OkHttp的Request对象,再封装成OkHttp.call
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          throwIfFatal(t);
          failure = creationFailure = t;
        }
      }
    }

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

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

// 步骤2:发送网络请求
    // delegate是OkHttpcall的静态代理
    // delegate静态代理最终还是调用Okhttp.enqueue进行网络请求
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
// 步骤3:解析返回数据
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }

        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }

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

3.1.5 如何进行最后线程切换
线程切换是通过一开始创建Retrofit对象时Platform在检测到运行环境是Android时进行创建的。

static class Android extends Platform {

    // 创建默认的回调执行器工厂
    // 如果不将RxJava和Retrofit一起使用,一般都是使用该默认的CallAdapter.Factory
    // 后面会对RxJava和Retrofit一起使用的情况进行分析
    @Override
      CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }

    @Override 
      public Executor defaultCallbackExecutor() {
      // 返回一个默认的回调方法执行器
      // 该执行器负责在主线程(UI线程)中执行回调方法
      return new MainThreadExecutor();
    }

    // 获取主线程Handler
    static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());


      @Override 
      public void execute(Runnable r) {
        // Retrofit获取了主线程的handler
        // 然后在UI线程执行网络请求回调后的数据显示等操作。
        handler.post(r);
      }
    }

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

推荐阅读更多精彩内容