Retrofit之知其所以然

知其所以然

源码分析首先从入口开始,先来回忆Retrofit的使用步骤:

1.创建Retrofit实例
2.创建 网络请求接口实例 并 配置网络请求参数
3.发送网络请求
4.解析网络请求完成服务器返回的数据

Retrofit流程

创建Retrofit实例

1,Builder设置参数
  Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://api.example.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();

Retrofit实例是通过建造者模式Builder类进行创建的,Builder类包含以下可以设置参数的方法:

    /**
     *  可提前通过OkHttpClient 设置基本参数类似baseUrl、各种拦截器等
     */
    public Builder client(OkHttpClient client) {
      return callFactory(checkNotNull(client, "client == null"));
    }

    /**
     *  网络请求器的工厂
     *  作用:生产网络请求器(Call)
     *  如果没有设置Retrofit是默认使用OkHttpClient
     */
    public Builder callFactory(okhttp3.Call.Factory factory) {
      this.callFactory = checkNotNull(factory, "factory == null");
      return this;
    }

    /**
     * Set the API base URL.
     *
     * @see #baseUrl(HttpUrl)
     */
    public Builder baseUrl(String baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      HttpUrl httpUrl = HttpUrl.parse(baseUrl);
      if (httpUrl == null) {
        throw new IllegalArgumentException("Illegal URL: " + baseUrl);
      }
      return baseUrl(httpUrl);
    }
    
    /**
     * 这个细讲
     */
    public Builder baseUrl(HttpUrl baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      List<String> pathSegments = baseUrl.pathSegments();
      if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
        throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
      }
      this.baseUrl = baseUrl;
      return this;
    }

    /**
    * 数据转换器工厂的集合
    * 作用:放置数据转换器工厂
    * 数据转换器工厂作用:生产数据转换器(converter)
    */
    public Builder addConverterFactory(Converter.Factory factory) {
      converterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }

    /**
     * 网络请求适配器工厂的集合
     * 作用:放置网络请求适配器工厂
     * 网络请求适配器工厂作用:生产网络请求适配器(CallAdapter) 
     * 如果没有设置默认使     ExecutorCallAdapterFactory
     * 后面在细说
     */
    public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
      adapterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }

    /**
     * 方法回调器,可以控制结果返回的线程等
     * 注意: {@code executor} 不是用作 {@linkplain #addCallAdapterFactory 方法的返回类型
     */
    public Builder callbackExecutor(Executor executor) {
      this.callbackExecutor = checkNotNull(executor, "executor == null");
      return this;
    }

    /**
     * Retrofit调用 {@link #create}时 , 设置成true这个配置可以提前对业务接口中的注解进行验证转换的标志  
     *位
     */
    public Builder validateEagerly(boolean validateEagerly) {
      this.validateEagerly = validateEagerly;
      return this;
    }

这里我们细讲下baseUrl,其他的后面调用的时候在做分析:Baseurl

baseUrl的设置还是有点讲究的,我们一般设置一个String类型的字符串,Retrofit会把String转换成符合OKHttp请求的HttpUrl,最好按规矩办事,不然容易出问题:

1,baseUrl应该以"/"结尾(非强制,但这样设置能避免很多问题)
2,方法设置的url只如果以“/”开头,默认就是绝对值,同样也可以设置一个完整的Url


baseUrl不以“/”结尾
正解: http://example.com/api/ + foo/bar/ = http://example.com/api/foo/bar/
错误: http://example.com/api + foo/bar/ = http://example.com/foo/bar/;

HttpUrl会通过各种手段BaseUrl截取最后一个“/”之前的内容作为BaseUrl

方法设置的url只如果以“/”开头
BaseUrlL: http://example.com/api/
方法设置的url: /foo/bar/
最终请求的完整url:http://example.com/foo/bar/

BaseUrl: http://example.com/
方法设置的url:/foo/bar/
最终请求的完整url:http://example.com/foo/bar/

如果方法设置的url以“/”开头则被认为是绝对值,最终的而结果是截取掉BaseUrl第一个“/”后面的部分在进行拼接

方法设置的url如果是个完成的url,baseUrl将不起作用
BaseUrl: http://example.com/
方法设置的url:https://github.com/square/retrofit/
最终请求的完整url:https://github.com/square/retrofit/

BaseUrl: http://example.com/
方法设置的url://github.com/square/retrofit/
最终请求的完整url:http://github.com/square/retrofit/(注意是http开头)

2,参数设置完成开始build方法

build方法的源码部分
build方法会在一些参数没有配置的情况下会创建一个默认的对象

   /**
     * Create the {@link Retrofit} instance using the configured values.
     * <p>
     * Note: If neither {@link #client} nor {@link #callFactory} is called a default {@link
     * OkHttpClient} will be created and used.
     */
    public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
      
      //如果没有自定义okhttp3.Call.Factory则默认使用OkHttpClient,OkHttp请求的关键类
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

      //回调方法执行,处理结果回调线程等,通过platform.defaultCallbackExecutor()默认拿到的是MainThreadExecutor
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // 创建防御类型的对象(避免对同一个对象的操作),如果没有设置则默认为ExecutorCallAdapterFactory,如果使用rxjava则需要设置RxJavaCallAdapterFactory
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      // 创建防御类型的数据解析对象(避免对同一个对象的操作)
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

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

在创建执行方法回调callbackExecutor 时用到 platform.defaultCallbackExecutor(),platform是Builder的构造方法里面创建的一个平台对象

Builder(Platform platform) {
      this.platform = platform;
      // Add the built-in converter factory first. This prevents overriding its behavior but also
      // ensures correct behavior when using converters that consume all types.
      converterFactories.add(new BuiltInConverters());
    }

    public Builder() {
      this(Platform.get());
    }

我们一般使用的时候不会去设置platform,因为默认的就是Android平台

private static final Platform PLATFORM = findPlatform();

  static Platform get() {
    return PLATFORM;
  }

  //通过findPlatform去寻找默认的平台
  private static Platform findPlatform() {
    try {
      Class.forName("android.os.Build");
      if (Build.VERSION.SDK_INT != 0) {
        //android开发这里是默认的平台
        return new Android();
      }
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("java.util.Optional");
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("org.robovm.apple.foundation.NSObject");
      //IOS都支持?
      return new IOS();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
  }
  
  //这里是android平台的实现
  static class Android extends Platform {
     //platform.defaultCallbackExecutor()调用,默认是MainThreadExecutor,这是一个将结果发送到主线程的回调方法执行器,也可以通过自定义处理
    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }
    
    //如果没有设置对rxjava(RxJavaCallAdapterFactory)的支持这里  默认ExecutorCallAdapterFactory是对Call对象的处理
    @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }

    //通过Looper.getMainLooper()拿到主线程的looper将结果发送到主线程
    static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

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

Retrofit还可以在其他的平台使用

平台包括:Android、Rxjava、Guava和java8,需要添加对应的RxJavaCallAdapterFactory,Java8
CallAdapterFactory等

2.配置网络请求参数

Retrofit实例创建之后会调用create方法创建interface对象,在通过接口对象调用相应的方法

public interface CategoryService {

        @GET("top250") 
        Call<MovieEntity> getcategoryList(@Query("start") int start, @Query("count") int count);
        
        @POST("/form")
        @FormUrlEncoded
        Call<ResponseBody> submit(@Field("username") String name, @Field("token") String token);
}

retrofit.create(CategoryService.class).getcategoryList(0,10);
create方法

create源码部分

 public <T> T create(final Class<T> service) {
    //检查是否为接口
    Utils.validateServiceInterface(service);
    //如果设置了validateEagerly为true,就会带调用eagerlyValidateMethods对接口中的所有方法进行提前配置
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          //这里还是拿到平台对象,和实例一样用Android平台的对象
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, Object... args)
              throws Throwable {
            // 如果是来自Object的方法则按调默认的invoke方法
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            //platform.isDefaultMethod 默认返回false,Android没有重写,固为false,java8倒是有重写这个方法
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            //这里就是loadServiceMethod配置ServiceMethod 
            ServiceMethod serviceMethod = loadServiceMethod(method);
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            //通过创建的serviceMethod对象里创建的CallAdapter的adapt方法去适配生成相应的网络请求代理 (Call,Obsevalble)
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

private void eagerlyValidateMethods(Class<?> service) {
    Platform platform = Platform.get();
    //拿到接口中的所有方法进行配置
    for (Method method : service.getDeclaredMethods()) {
      if (!platform.isDefaultMethod(method)) {
        loadServiceMethod(method);
      }
    }
  }

ServiceMethod loadServiceMethod(Method method) {
    ServiceMethod result;
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        //将参数配置给ServiceMethod
        result = new ServiceMethod.Builder(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

create创建相应的接口对象后调用getcategoryList方法会通过InvocationHandler 配置ServiceMethod对象,并通过CallAdapter的adpt方法生成相应的Call或Observable。
这里主要涉及到ServiceMethod的配置问题需要详细说明

1,Retrofit的参数传递给ServiceMethod
2,方法的注解和方法参数的注解解析并复制给ServiceMethod的参数

so,ServiceMethod包含了网络请求的所有参数成了全村人的希望

关于ServiceMethod
public Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      //拿到方法上面的所有注解
      this.methodAnnotations = method.getAnnotations();
      //拿到方法的所有泛型类型
      this.parameterTypes = method.getGenericParameterTypes();
      //拿到方法参数的所有注解,注意是参数的注解,这是一个二维数组,表示一个参数可以有两个注解,第一个下标志表示所有参数的下标,第二个参数表示一个参数中其中一个注解的下标
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }

public ServiceMethod build() {
      //创建一个CallAdapter对象最后会调用他的adapt方法创建Call或ObserVable
      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);
      }
      //请求方法不能为空,比如GET,POST必须有一个
      if (httpMethod == null) {
        throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
      }
      
      //如果注解是@GET则@Multipart和@FormEncoded都不能存在
      if (!hasBody) {
        if (isMultipart) {
          throw methodError(
              "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
        }
        if (isFormEncoded) {
          throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
              + "request body (e.g., @POST).");
        }
      }

      //下面是对参数注解的解析
      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);
      }
      
      //没有设置url(在方法注解和参数注解都没有设置url)
      if (relativeUrl == null && !gotUrl) {
        throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
      }

      //@Body注解的必须同时存在@Multipart或@FormEncoded或POST放等其中一种
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError("Non-body HTTP method cannot contain @Body.");
      }

      //@FormEncoded注解至少要一个参数注解是@Field
      if (isFormEncoded && !gotField) {
        throw methodError("Form-encoded method must contain at least one @Field.");
      }
      
       //@Multipart注解至少要一个参数注解是@Part
      if (isMultipart && !gotPart) {
        throw methodError("Multipart method must contain at least one @Part.");
      }

      return new ServiceMethod<>(this);
    }

关于CallAdapter

ServiceMethod对象生成以后会利用CallAdapter的adapter方法生成Call对象,典型的适配器模式
CallAdapter的生成在ServiceMethod中

private CallAdapter<?> createCallAdapter() {
      Type returnType = method.getGenericReturnType();
      //方法的返回类型中泛型不能是T,K等变量或者通配符
      if (Utils.hasUnresolvableType(returnType)) {
        throw methodError(
            "Method return type must not include a type variable or wildcard: %s", returnType);
      }

     //void也不行
      if (returnType == void.class) {
        throw methodError("Service methods cannot return void.");
      }
      Annotation[] annotations = method.getAnnotations();
      try {
        return 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);
      }
    }
  
  public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
  }
  
  public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    checkNotNull(returnType, "returnType == null");
    checkNotNull(annotations, "annotations == null");
    
    拿到第一个null后的值,如果集合中没有null则start 为0
    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;
      }
    }
    
    //这里后面操作了一堆只是为了抛一个异常
    StringBuilder builder = new StringBuilder("Could not locate call adapter for ")
        .append(returnType)
        .append(".\n");
    if (skipPast != null) {
      builder.append("  Skipped:");
      for (int i = 0; i < start; i++) {
        builder.append("\n   * ").append(adapterFactories.get(i).getClass().getName());
      }
      builder.append('\n');
    }
    builder.append("  Tried:");
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      builder.append("\n   * ").append(adapterFactories.get(i).getClass().getName());
    }
    throw new IllegalArgumentException(builder.toString());
  }

adapterFactories默认是ExecutorCallAdapterFactory,他的get方法

通过get方法生成CallAdapter
@Override
  public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }
      
      //最后通过adapt方法创建Call<R> 的实现类ExecutorCallbackCall
      @Override public <R> Call<R> adapt(Call<R> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }

创建的CallAdapter调用adapt方法创建了Call的ExecutorCallbackCall实现类

3.发送网络请求并处理结果

Call call = retrofit.create(CategoryService.class).getcategoryList(0,10);

 call.enqueue(new Callback<MovieEntity>() {
        @Override
        public void onResponse(Call<MovieEntity> call, Response<MovieEntity> response) {
            resultTV.setText(response.body().toString());
        }

        @Override
        public void onFailure(Call<MovieEntity> call, Throwable t) {
            resultTV.setText(t.getMessage());
        }
    });

这里的call实际是Call的实现类ExecutorCallbackCall,ExecutorCallbackCall的源码

   static final class ExecutorCallbackCall<T> implements Call<T> {
    final Executor callbackExecutor;
    final Call<T> delegate;
    
    //这里参数传进来的是MainThreadExecutor,和OkHttpCall对象
    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
    }
    
    //当调用enqueue方法时
    @Override public void enqueue(final Callback<T> callback) {
      if (callback == null) throw new NullPointerException("callback == null");
      代理类OkHttpCall调用enqueue
      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);
            }
          });
        }
      });
    }

    @Override public boolean isExecuted() {
      return delegate.isExecuted();
    }

    @Override public Response<T> execute() throws IOException {
      return delegate.execute();
    }

    @Override public void cancel() {
      delegate.cancel();
    }

    @Override public boolean isCanceled() {
      return delegate.isCanceled();
    }

    @SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone.
    @Override public Call<T> clone() {
      return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone());
    }

    @Override public Request request() {
      return delegate.request();
    }
  }

代理模式一层套一层
关于OkHttpCall 的enqueue方法源码

@Override public void enqueue(final Callback<T> callback) {
    if (callback == null) throw new NullPointerException("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();
    }
    
    //这里就最终和进入到Okhttp的调用流程
    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();
        }
      }

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

createRawCall是关键的创建 okhttp3.Call对象的方法

 private okhttp3.Call createRawCall() throws IOException {
    //把serviceMethod中所有的参数都赋值给Request,毕竟 Request才是OkHttp想要的
    Request request = serviceMethod.toRequest(args);
    //这里serviceMethod.callFactory是oKHttpClient,后面newCall就是OkHttp创建okhttp3.Call 的方法了
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,978评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,954评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,623评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,324评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,390评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,741评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,892评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,655评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,104评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,569评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,254评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,834评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,725评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,950评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,260评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,446评论 2 348

推荐阅读更多精彩内容

  • 首先这三次课让我在夫妻关系,亲子关系中有了新认知,并依据新认知作出了某些尝试,在尝试过程中又产生了新矛盾,我想到这...
    分裂分裂阅读 136评论 0 0
  • 今天一如既往 路还是昨天的路 太阳还是昨天的太阳 有时候想 明天该去往何方 是否也会为今天的你心伤
    几个你啊y阅读 203评论 0 0
  • leave: If an event leaves people or things in a particula...
    gltjk阅读 176评论 0 0
  • 在生活中,我认为环境对人的影响很大。 一是,先来举一个例子,大家都听说过“孟母三迁”的故事吧。里面讲的是战国的时候...
    田浩甲MAX阅读 725评论 0 0