Retrofit学习(一)

一. 使用用例

    //步骤【1】
    OkHttpclient client = new OkHttpClient.Builder().build();
    Retrofit retrofit = new Retrofit.Builder()
        .client(client)
        .baseUrl("http://localhost:4567/")
        .build();
    
    
    @GET("/getName")
    Call<ResponseBody> request(@Query("name") String name);
    
    //步骤【2】
    IService service = retrofit.create(IService.class); 
    Call<ResponseBody> call = service.request("test");
    //步骤【3】
    call.enqueue(new Callable() {
        ...
    )  
        

二. 源码解析

1. Retrofit.Build


    //一般情况下,Platform是Android
    public Builder() {
       this(Platform.get());
    }
    
    Builder(Platform platform) {
            this.platform = platform;
            converterFactories.add(new BuiltInConverters());
        }
    
    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;
        //如果是Android平台,则Executor的实际逻辑就是在当前主线程中runnable
        if (callbackExecutor == null) {
            callbackExecutor = platform.defaultCallbackExecutor();
        }

        List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
        //如果是Android平台,defaultCallAdapterFactory是ExecutorCallAdapterFactory
            adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

       //如果没设置,默认没有convert
       List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

       //validateEagerly默认为false
       return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
                    callbackExecutor, validateEagerly);
}

  1. 如果没有设置CallAdapter,对于Android平台,默认使用ExecutorCallAdapterFactory
  2. 如果没有设置ConvertFactory, 默认使用BuiltInConverters

2. Retrofit.create


    public <T> T create(final Class<T> service) {
        //校验传入的Class是不是Interface,且定义了至少一个方法
        Utils.validateServiceInterface(service);
        if (validateEagerly) {
            //如果是Android平台,isDefaultMethod = false, 将service中所有method都生成对应的
            //ServiceMethod类,并放入缓存(核心调用为【2.1】loadServiceMethod)
            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 (method.getDeclaringClass() == Object.class) {
                            return method.invoke(this, args);
                        }
                        //Android平台isDefaultMethod = false
                        if (platform.isDefaultMethod(method)) {
                            return platform.invokeDefaultMethod(method, service, proxy, args);
                        }
                        ServiceMethod<Object, Object> serviceMethod =
                                (ServiceMethod<Object, Object>) loadServiceMethod(method);
                        OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
                        return serviceMethod.callAdapter.adapt(okHttpCall);
                    }
                });
    }
  1. 首先校验传入的Class是不是一个接口
  2. 因为默认的validateEagly = false, 所以不执行eagerlyValidateMethods方法,该方法实质上根据接口中的所有方法生成每个方法相应的ServiceMethod,并放入缓存
  3. 创建动态代理(核心步骤),以后每次调用接口的方法都会调用InvocationHandler.invoke(...)方法

2.1 Retrofit.loadServiceMethod

    ServiceMethod<?, ?> loadServiceMethod(Method method) {
        ServiceMethod<?, ?> result = serviceMethodCache.get(method);
        if (result != null)
            return result;

        synchronized (serviceMethodCache) {
            result = serviceMethodCache.get(method);
            if (result == null) {
                //【2.2】
                result = new ServiceMethod.Builder<>(this, method).build();
                serviceMethodCache.put(method, result);
            }
        }
        return result;
    }

创建相应的ServiceMethod类,并放入缓存

2.2 ServiceMethod.Builder


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, 【2.2.1】
     callAdapter = createCallAdapter();
     //获取Call<T>中T的具体类型
     responseType = callAdapter.responseType();
     //确保T并不是retrofit2.Response或者okhttp3.Response
     if (responseType == Response.class||responseType == okhttp3.Response.class) {
        ...
     }
     //如果没有设置Converter,默认的Converter是BuiltInConverter, 通过
     //BuiltInConvert.responseBodyConverter返回Convert, 
     //如果Annotation中Streaming, 则返回StreamingResponseBodyConverter, 
     //否则返回BufferingResponseBodyConverter 
     //【2.2.2】
     responseConverter = createResponseConverter();
     //处理方法的注释, 【2.2.3】
     for (Annotation annotation : methodAnnotations) {
          parseMethodAnnotation(annotation);
     }
     if (httpMethod == null) {
           throw methodError(...);
     }

    //对于GET,HEAD等没有body的请求,不允许出现@FormEncodedUrl或@MultiPart等注释
     if (!hasBody) {
         if (isMultipart) {
                throw methodError(..);
         }
         if (isFormEncoded) {
               throw methodError(...);
         }
      }

     //处理参数的注解, 【2.2.4】
     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(...);
            }

            Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
            if (parameterAnnotations == null) {
                  throw parameterError(...);
            }

            parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
       }

       if (relativeUrl == null && !gotUrl) {
              throw methodError(...);
       }
       //GET, DELETE等方法不可以有@Body
       if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
             throw methodError(...);
       }
       //有@FormUrlEncoded注释则参数中至少有一个@Field注释
       if (isFormEncoded && !gotField) {
             throw methodError(...);
       }
       //有@Multipart注释,参数中至少有一个@Part注释
       if (isMultipart && !gotPart) {
             throw methodError(...);
       }
       return new ServiceMethod<>(this);
 }
  1. 获取CallAdapter
  2. 获取Call<T>中T的具体类型,并确保T并不是retrofit2.Reponse或okhttp3.Response
  3. 获取Converter
  4. 处理方法注释
  5. 注释参数注释

2.2.1 ServiceMethod.createCallAdapter

private CallAdapter<T, R> createCallAdapter() {
      Type returnType = method.getGenericReturnType();
       //返回值不能是TypeVariable和WildcardType
       if (Utils.hasUnresolvableType(returnType)) {
             throw methodError(...);
       }
       //确保返回值不能是void
       if (returnType == void.class) {
            throw methodError(...);
      }
      Annotation[] annotations = method.getAnnotations();
      try {
             return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
      } catch (RuntimeException e) { 
             throw methodError(...);
      }
}
2.2.1.1 Retrofit.callAdapter

    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");

        int start = adapterFactories.indexOf(skipPast) + 1;
        for (int i = start, count = adapterFactories.size(); i < count; i++) {
            //如果没有显示的设置,Android平台的默认只有一个CallAdapter.Factory -> ExecutorCallAdapterFactory
            CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
            if (adapter != null) {
                return adapter;
            }
        }

        ...        
        throw new IllegalArgumentException(...);
    }

2.2.1.2 ExecutorCallAdapterFactory.get()

    @Override
    public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        //确保返回值类型是Call
        if (getRawType(returnType) != Call.class) {
            return null;
        }
        //返回Call<T>的具体所指的类型
        final Type responseType = Utils.getCallResponseType(returnType);
        return new CallAdapter<Object, Call<?>>() {
            @Override
            public Type responseType() {
                return responseType;
            }

            @Override
            public Call<Object> adapt(Call<Object> call) {
                return new ExecutorCallbackCall<>(callbackExecutor, call);
            }
        };
    }


2.2.2 ServiceMethod.createResponseConverter()


    private Converter<ResponseBody, T> createResponseConverter() {
            Annotation[] annotations = method.getAnnotations();
            try {
                return retrofit.responseBodyConverter(responseType, annotations);
            } catch (RuntimeException e) { 
                 throw methodError(...);
            }
        }

2.2.2.1 Retrofit.responseBodyConverter

    public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
        return nextResponseBodyConverter(null, type, annotations);
    }
    
    public <T> Converter<ResponseBody, T> nextResponseBodyConverter(Converter.Factory skipPast,Type type, Annotation[] annotations) {
    
        checkNotNull(type, "type == null");
        checkNotNull(annotations, "annotations == null");

        int start = converterFactories.indexOf(skipPast) + 1;
        for (int i = start, count = converterFactories.size(); i < count; i++) {
            //如果没有显示设置, convertFactories里面只有BuiltInConverters
            Converter<ResponseBody, ?> converter =
                    converterFactories.get(i).responseBodyConverter(type, annotations, this);
            if (converter != null) {
                return (Converter<ResponseBody, T>) converter;
            }
        }

        ...
        throw new IllegalArgumentException(...);
    }

2.2.2.2 BuiltInConverters.responseBodyConverter

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
                                                            Retrofit retrofit) {
        if (type == ResponseBody.class) {
            return Utils.isAnnotationPresent(annotations, Streaming.class)
                    ? StreamingResponseBodyConverter.INSTANCE
                    : BufferingResponseBodyConverter.INSTANCE;
        }
        if (type == Void.class) {
            return VoidResponseBodyConverter.INSTANCE;
        }
        return null;
    }

2.2.3 ServiceMethod.parseMethodAnnotation


    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());
            }
            else if (annotation instanceof retrofit2.http.Headers) {
                String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
                if (headersToParse.length == 0) {
                    throw methodError("@Headers annotation is empty.");
                }
                headers = parseHeaders(headersToParse);
            }
            else if (annotation instanceof Multipart) {
                if (isFormEncoded) {
                    throw methodError("Only one encoding annotation is allowed.");
                }
                isMultipart = true;
            }
            else if (annotation instanceof FormUrlEncoded) {
                if (isMultipart) {
                    throw methodError("Only one encoding annotation is allowed.");
                }
                isFormEncoded = true;
            }
        }

parseMethodAnnotation方法主要就是用过parseHttpMethodAndPath方法设置ServiceMethhod的请求方法,相对Url, 是否有body等选项

2.2.4 ServiceMethod.parseParameter

private ParameterHandler<?> parseParameter(int p, Type parameterType, Annotation[] annotations) {
     ParameterHandler<?> result = null;
     for (Annotation annotation : annotations) {
          //核心调用
         ParameterHandler<?> annotationAction = parseParameterAnnotation(
                        p, parameterType, annotations, annotation);
         if (annotationAction == null) {
              continue;
          }
         if (result != null) {
              throw parameterError(...);
         }
         result = annotationAction;
     }

    if (result == null) {
        throw parameterError(p, "No Retrofit annotation found.");
    }
    return result;
}

该方法中的核心调用parseParameterAnnotation,这个方法的主要作用是根据参数的注解进行不同的处理,参数的注解可以是以下几种:

  • @Url
  • @Path
  • @Query
  • @QueryName
  • @QueryMap
  • @Header
  • @HeaderMap
  • @Field
  • @FieldMap
  • @Part
  • @PartMap
  • @Body

2.3 ServiceMethod.callAdapter.adapter

ServiceMethod.callAdapter是通过ExcecutorAdapterFactory.get()创建的一个匿名CallAdapter, 其实现为:


    new CallAdapter<Object, Call<?>>() {
            @Override
            public Type responseType() {
                return responseType;
            }

            @Override
            public Call<Object> adapt(Call<Object> call) {
                return new ExecutorCallbackCall<>(callbackExecutor, call);
            }
        };
        
    static final class ExecutorCallbackCall<T> implements Call<T> {
        final Executor callbackExecutor;
        final Call<T> delegate;
        ....
    }

由此可以看出,最终真正返回的是ExecutorCallbackCall, 其中delagateOkHttpCall

3. ExecutorCallbackCall.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()) {
                                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);
                        }
                    });
                }
       });
    }

可以看到实际调用的是OkHttpCall.enqueue

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

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

        if (canceled) {
            call.cancel();
        }
        //调用okhttp3.Call.enqueue,后续走的就是okhttp请求的流程
        call.enqueue(new okhttp3.Callback() {
            @Override
            public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
                    throws IOException {
                Response<T> response;
                try {
                    //【3.1.2】
                    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();
                }
            }
        });
    }

3.1.1 OkHttpCall.createNewCall

    private okhttp3.Call createRawCall() throws IOException {
        //这里的args就是被动态代理的方法的参数, 【3.1.2.1】
        Request request = serviceMethod.toRequest(args);
        //callFactory是OkHttpClient
        okhttp3.Call call = serviceMethod.callFactory.newCall(request);
        if (call == null) {
            throw new NullPointerException("Call.Factory returned null.");
        }
        return call;
    }

OkHttpClient同样实现了okhttp3.Call.Factory接口, 而通过步骤【1】和【2.2】的代码可以看出, 这里的callFactory就是OkHttpClient,所以调用的是OkhttpClient.newCall(实际返回一个okhttp3.RealCall)

3.1.1.1 ServiceMethod.toRequest

    Request toRequest(Object... args) throws IOException {
        RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
                contentType, hasBody, isFormEncoded, isMultipart);

         ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

        int argumentCount = args != null ? args.length : 0;
        if (argumentCount != handlers.length) {
            throw new IllegalArgumentException("Argument count (" + argumentCount
                    + ") doesn't match expected count (" + handlers.length + ")");
        }

        for (int p = 0; p < argumentCount; p++) {
            //不同参数的注释有对应不同的handler, 以Query为例,其handler是Query<T>
            handlers[p].apply(requestBuilder, args[p]);
        }

        return requestBuilder.build();
    }

这个方法的核心调用在于handlers[p].apply,不同的参数注释,对应这不同的ParameterHandler,以@Query为例,它所对应的ParammeterHandlerQuery<T>, Query<T>.apply的作用是将@Query所修饰的参数转为String类型并添加到要请求的url的query string中

3.1.2 OkHttpCall.parseResponse

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

        // Remove the body's source (the only stateful object) so we can pass the response along.
        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) {
            rawBody.close();
            return Response.success(null, rawResponse);
        }

        ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
        try {
            T body = serviceMethod.toResponse(catchingBody);
            return Response.success(body, rawResponse);
        } catch (RuntimeException e) {
            // If the underlying source threw an exception, propagate that rather than indicating it was
            // a runtime exception.
            catchingBody.throwIfCaught();
            throw e;
        }
    }

3.1.3 ServiceMethod.toResponse

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

在之前【2.2.2】中可以得知, 如果没有设置Converter,则使用默认的BuiltInConverter来获取对应的response convert, 如果方法中有@Streaming注释,则responseConverterStreamingResponseBodyConverter, 否则是BufferingResponseBodyConverter, 一般情况下不会有@Streaming注释

3.1.4 Converter.convert

3.1.4.1 StreamingResponseBodyConverter
    static final class StreamingResponseBodyConverter
            implements Converter<ResponseBody, ResponseBody> {
        static final StreamingResponseBodyConverter INSTANCE = new StreamingResponseBodyConverter();

        @Override
        public ResponseBody convert(ResponseBody value) throws IOException {
            return value;
        }
    }

3.1.4.2 BufferingResponseBodyConverter
static final class BufferingResponseBodyConverter
            implements Converter<ResponseBody, ResponseBody> {
        static final BufferingResponseBodyConverter INSTANCE = new BufferingResponseBodyConverter();

    @Override
    public ResponseBody convert(ResponseBody value) throws IOException {
       try {
            // Buffer the entire body to avoid future I/O.
            return Utils.buffer(value);
        } finally {
            value.close();
        }
    }
}
    
//Utils.buffer 
static ResponseBody buffer(final ResponseBody body) throws IOException {
    Buffer buffer = new Buffer();
    body.source().readAll(buffer);
    return ResponseBody.create(body.contentType(), body.contentLength(), buffer);
}

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

推荐阅读更多精彩内容