OkHttp 的进化 -- Retrofit

代码示例从github上获取(不是自己写的 ):https://github.com/jdsjlzx/RetrofitDemo

先看OKHttp的使用,最简单的:

//1.创建OkHttpClient对象
OkHttpClient okHttpClient = new OkHttpClient();
//2.创建Request对象,设置一个url地址(百度地址),设置请求方式。
Request request = new Request.Builder().url("http://www.baidu.com").method("GET",null).build();
//3.创建一个call对象,参数就是Request请求对象
Call call = okHttpClient.newCall(request);
//4.请求加入调度,重写回调方法
    call.enqueue(new Callback() {
  //请求失败执行的方法
  @Override
  public void onFailure(Call call, IOException e) {
  }
  //请求成功执行的方法
  @Override
  public void onResponse(Call call, Response response) throws IOException {
  }
});

主要涉及3个类:

  • OkHttpClient
  • RealCall
  • Request
  public OkHttpClient() {
    this(new Builder());
  }
 ... ...
public static final class Builder {
    Dispatcher dispatcher;
    Proxy proxy;
    List<Protocol> protocols;
    List<ConnectionSpec> connectionSpecs;
    final List<Interceptor> interceptors = new ArrayList<>();
    final List<Interceptor> networkInterceptors = new ArrayList<>();
    ProxySelector proxySelector;
    CookieJar cookieJar;
    Cache cache;
    InternalCache internalCache;
    SocketFactory socketFactory;
    SSLSocketFactory sslSocketFactory;
    TrustRootIndex trustRootIndex;
    HostnameVerifier hostnameVerifier;
    CertificatePinner certificatePinner;
    Authenticator proxyAuthenticator;
    Authenticator authenticator;
    ConnectionPool connectionPool;
    Dns dns;
    boolean followSslRedirects;
    boolean followRedirects;
    boolean retryOnConnectionFailure;
    int connectTimeout;
    int readTimeout;
    int writeTimeout;

 public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      proxySelector = ProxySelector.getDefault();
      cookieJar = CookieJar.NO_COOKIES;
      socketFactory = SocketFactory.getDefault();
      hostnameVerifier = OkHostnameVerifier.INSTANCE;
      certificatePinner = CertificatePinner.DEFAULT;
      proxyAuthenticator = Authenticator.NONE;
      authenticator = Authenticator.NONE;
      connectionPool = new ConnectionPool();
      dns = Dns.SYSTEM;
      followSslRedirects = true;
      followRedirects = true;
      retryOnConnectionFailure = true;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
    }

通过这些代码我们可以知道OkHttpClient中包含了在HTTP网络访问的时候需要的一些参数值,为了使用方便,有些参数值由默认值设置:

 socketFactory = SocketFactory.getDefault();

   public static SocketFactory getDefault()
    {
        synchronized (SocketFactory.class) {
            if (theFactory == null) {
                //
                // Different implementations of this method SHOULD
                // work rather differently.  For example, driving
                // this from a system property, or using a different
                // implementation than JavaSoft's.
                //
                theFactory = new DefaultSocketFactory();
            }
        }

        return theFactory;
    }
   dns = Dns.SYSTEM;
   public interface Dns {
  /**
   * A DNS that uses {@link InetAddress#getAllByName} to ask the underlying operating system to
   * lookup IP addresses. Most custom {@link Dns} implementations should delegate to this instance.
   */
  Dns SYSTEM = new Dns() {
    @Override public List<InetAddress> lookup(String hostname) throws UnknownHostException {
      if (hostname == null) throw new UnknownHostException("hostname == null");
      return Arrays.asList(InetAddress.getAllByName(hostname));
    }
  };
...  ...

这段代码中我们可以看出,Request 类最主要的就是包含HTTP请求所需要的参数,主要参数为:
private final HttpUrl url;
private final String method;
private final Headers headers;
private final RequestBody body;
private final Object tag;

为了使用的方便,Request 的提供了一个Request .Builder 构造类。

public final class Request {
  private final HttpUrl url;
  private final String method;
  private final Headers headers;
  private final RequestBody body;
  private final Object tag;

  ...  ...  ...

  private Request(Builder builder) {
    this.url = builder.url;
    this.method = builder.method;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.tag = builder.tag != null ? builder.tag : this;
  }
  
 
 public static class Builder {
    private HttpUrl url;
    private String method;
    private Headers.Builder headers;
    private RequestBody body;
    private Object tag;

    public Builder() {
      this.method = "GET";
      this.headers = new Headers.Builder();
    }

    private Builder(Request request) {
      this.url = request.url;
      this.method = request.method;
      this.body = request.body;
      this.tag = request.tag;
      this.headers = request.headers.newBuilder();
    }
    
    ... 
    
      public Request build() {
      if (url == null) throw new IllegalStateException("url == null");
      return new Request(this);
    }
    ... ... ... 
   } 
}

在创建好 OkHttpClient 和 Request之后,RealCall开始登场了

final class RealCall implements Call {
  private final OkHttpClient client;
... ... ...
  /** The application's original request unadulterated by redirects or auth headers. */
  Request originalRequest;
  HttpEngine engine;

  protected RealCall(OkHttpClient client, Request originalRequest) {
    this.client = client;
    this.originalRequest = originalRequest;
  }

紧接着就是执行HTTP请求了,这里提供了两种方式,立即执行 和 加入执行队列

@Override public Response execute() throws IOException {
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  try {
    client.dispatcher().executed(this);
    Response result = getResponseWithInterceptorChain(false);
    if (result == null) throw new IOException("Canceled");
    return result;
  } finally {
    client.dispatcher().finished(this);
  }
}
  
void enqueue(Callback responseCallback, boolean forWebSocket) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
}

这里我们可以看到一个关于管理调度的标志性代码:client.dispatcher().XXX
我们看看这个是如何做到的 --- ExecutorService 线程池

public final class Dispatcher {
... ...
 public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }
... ...

至于剩下的AsyncCall肯定是 Runnable 。这个就不贴代码了

总结下:OkHttp 的逻辑很简单,

  • OkHttpClient 中包含了网络请求中所需要的所有变量参数,例如:DNS,proxy,connectTimeout 等
  • Request 中包含了 请求是需要的 URL,请求方式(get/Post 等), Header, body
  • OkHttpClient 与 Request 作为 RealCall 的成员变量,同时RealCall 作为一个可执行的TASK,将自身交给调度器去调度
  • 调度器就是 ThreadPoolExecutor 和 SynchronousQueue 组成

如果还有不明白的,可以依据这4条再回过头看看前面的代码

到此为止,可以开始正题了 :

Retrofit 到底干什么的?-- 其实就是OkHttp的一个封装库
Retrofit 为什么要封装?
Retrofit 的框架什么样的?
为什要设计成这样?
我们在编码中有什么可以借鉴的吗?

Retrofit 使用的最大的两个技术就是: 动态代理 和 注解

动态代理:java.lang.reflect.Proxy:这是 Java 动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。

先看看动态代理的相关代码

public interface PostRequest_Interface {
    @POST("translate?doctype=json&jsonversion=&type=&keyfrom=&model=&mid=&imei=&vendor=&screen=&ssid=&network=&abtest=")
    @FormUrlEncoded
     Call<Translation1> getCall(@Field("i") String targetSentence);
}

这里就有了interface getCall

public final class Retrofit {

... ...

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() {
        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);
          }
           if (platform.isDefaultMethod(method)) {
            return platform.invokeDefaultMethod(method, service, proxy, args);
          }
          Log.v("jar-filter", "method : " + method);
          ServiceMethod serviceMethod = loadServiceMethod(method);
          OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
          Log.v("jar-filter", "args : " + args);
          return serviceMethod.callAdapter.adapt(okHttpCall);
        }
      });
}

上面这段代码就是动态代理了,代理了接口 getCall ( @POST("translate?doctype=json&jsonversion=&type=&keyfrom=&model=&mid=&imei=&vendor=&screen=&ssid=&network=&abtest=")这个也算在里面)

我们再回头仔细分析下代码

Retrofit.java

 public <T> T create(final Class<T> 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, Object... args)
              throws Throwable {
             ... ... ...
            ServiceMethod serviceMethod = loadServiceMethod(method);
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
     }
  • ServiceMethod serviceMethod = loadServiceMethod(method);
    创建了ServiceMethod 的对象实例

  • OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
    创建了OkHttpCall 的对象实例

  • return serviceMethod.callAdapter.adapt(okHttpCall);
    返回一个ServiceMethod 的对象实例中的callAdapter.adapt

PostRequest_Interface request = retrofit.create(PostRequest_Interface.class);

//对 发送请求 进行封装(设置需要翻译的内容)
Call<Translation1> call = request.getCall("I love you");
//步骤6:发送网络请求(异步)
call.enqueue(new Callback<Translation1>() {

Proxy.newProxyInstance 函数,是在request.getCall的时候触发。
也就是说 return serviceMethod.callAdapter.adapt(okHttpCall);返回的就是Call<Translation1> call。

我拿到call后就直接enqueue。这个就说明call具有网络HTTP的功能。

loadServiceMethod

ServiceMethod loadServiceMethod(Method method) {
  ServiceMethod result;
  synchronized (serviceMethodCache) {
    result = serviceMethodCache.get(method);
    if (result == null) {
     //ServiceMethod 有一个Builder,实例的创建是通过创建者模式
      result = new ServiceMethod.Builder(this, method).build();
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}

从ServiceMethod的成员变量,可以看出ServiceMethod

  • 提供了了call的工厂 ---- okhttp3.Call.Factory callFactory;
  • 请求URL ---- HttpUrl baseUrl;
  • 请求头信息 --- Headers headers;
final class ServiceMethod<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;
  final CallAdapter<?> callAdapter;

  private final HttpUrl baseUrl;
  private final Converter<ResponseBody, T> responseConverter;
  private final String httpMethod;
  private final String relativeUrl;
  private final Headers headers;
  private final MediaType contentType;
  private final boolean hasBody;
  private final boolean isFormEncoded;
  private final boolean isMultipart;
  private final ParameterHandler<?>[] parameterHandlers;
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 = 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);
      }
      
      ... ...
      
      
     //这里注解 POST GET 等注解
    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());
        
        
        ... ...
        
      //可以将 注解转化为 HTTP请求时候需要的参数  httpMethod  hasBodyBuilder 等
     private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
      if (this.httpMethod != null) {
        throw methodError("Only one HTTP method is allowed. Found: %s and %s.",
            this.httpMethod, httpMethod);
      }

      this.httpMethod = httpMethod;
      this.hasBodyBuilder = hasBody;

      if (value.isEmpty()) {
        return;
      }

      // Get the relative URL path and existing query string, if present.
      int question = value.indexOf('?');
       if (question != -1 && question < value.length() - 1) {
        // Ensure the query string does not have any named parameters.
        String queryParams = value.substring(question + 1);
        Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
        if (queryParamMatcher.find()) {
          throw methodError("URL query string \"%s\" must not have replace block. "
              + "For dynamic query parameters use @Query.", queryParams);
        }
      }

      this.relativeUrl = value;
      this.relativeUrlParamNames = parsePathParameters(value);
    }
     ... ...  

从上述代码我们大致的可以猜测,ServiceMethod 是为HTTP强求前期准备资源的一个类。既包括 HTTP 资源参数,也包括 callFactory。
但是到目前为止,我们还没有与OkHttp 彻底的关联上。

new OkHttpCall<>(serviceMethod, args)

final class OkHttpCall<T> implements Call<T> {
  private final ServiceMethod<T> serviceMethod;
  private final Object[] args;

  private volatile boolean canceled;

    //注意这个,rawCall应该最终可以调用OkHttp 的 RealCall
  private okhttp3.Call rawCall;
  private Throwable creationFailure; // Either a RuntimeException or IOException.
  private boolean executed;

  /构造函数
  OkHttpCall(ServiceMethod<T> serviceMethod, Object[] args) {
    this.serviceMethod = serviceMethod;
    this.args = args;
  }
     

serviceMethod.callAdapter.adapt(okHttpCall)

首先要找到serviceMethod.callAdapter是什么
通过下面这一堆代码,我们可以知道 callAdapter 的 类就是 ExecutorCallAdapterFactory.ExecutorCallbackCall

// ServiceMethod.Builder 
public ServiceMethod build() {
      callAdapter = createCallAdapter();
      
// ServiceMethod.Builder      
private CallAdapter<?> createCallAdapter() {
    ... ...
    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);
    }
}



//retrofit.class
public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) {
  return nextCallAdapter(null, returnType, annotations);
}
  
//retrofit.class  
public CallAdapter<?> nextCallAdapter(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 可以有多个
    CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
    if (adapter != null) {
      return adapter;
    }
  }
  
  
  public Retrofit build() {
     
  ... ...
  // 在Retrofit创建的时候会给一个默认的 platform.defaultCallAdapterFactory(callbackExecutor)
  List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
  adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

  // Make a defensive copy of the converters.
  List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

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

//就是这个
 CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
    if (callbackExecutor != null) {
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }
    return DefaultCallAdapterFactory.INSTANCE;
  }
final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
  final Executor callbackExecutor;
  
  ... ...
final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
  final Executor callbackExecutor;
  
  ... ...
  
   @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;
      }

            //返回的是ExecutorCallAdapterFactory.ExecutorCallbackCall
      @Override public <R> Call<R> adapt(Call<R> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }

  ... ...

最后一步

call.enqueue(new Callback<Translation1>()
也就是
ExecutorCallAdapterFactory.ExecutorCallbackCall.enqueue(new Callback<Translation1>()

ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
    this.callbackExecutor = callbackExecutor;
    this.delegate = delegate;
  }

  @Override public void enqueue(final Callback<T> callback) {
    if (callback == null) throw new NullPointerException("callback == null");

    delegate.enqueue(new Callback<T>() {
    
    ... ...
      


  @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 {
        //
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          failure = creationFailure = t;
        }
      }
    }

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

    if (canceled) {
      call.cancel();
    }
 
   //这里就是真的 realcall.enqueue
    call.enqueue(new okhttp3.Callback() {      
      
      
private okhttp3.Call createRawCall() throws IOException {
//创建出 OkHttp  的request 
  Request request = serviceMethod.toRequest(args);
 //请参考  okhttp3.Call.Factory callFactory
  okhttp3.Call call = serviceMethod.callFactory.newCall(request);
  if (call == null) {
    throw new NullPointerException("Call.Factory returned null.");
  }
  return call;
}


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

总结

  • Retrofit 到底干什么的?
    其实就是OkHttp的一个封装库,更准确说是“HTTP控件库们”的封装库,但是还没有和OKHTTP 完全脱藕
  • Retrofit 为什么要封装?
    因为是HTTP控件们的封装库,可以支持多个HTTP库,都有封装脱藕
  • Retrofit 的框架什么样的?为什要设计成这样?
    注解 @GET @POST 等,来代替请求模式和参数
    ServiceMethod 是服务类,OkHttpCall 就是 OKHTTP库的实现类
  • 我们在编码中有什么可以借鉴的吗?
    其实注解功能可以更好的脱藕,但是代价比较大,解析代码太费劲了、
    在只有Okhttp一个库的情况下,没有很好的实际意义,如果同时指出 volley 等多个HTTP库的时候,就比较有意义了。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容