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, 所以不執(zhí)行eagerlyValidateMethods方法,該方法實質上根據(jù)接口中的所有方法生成每個方法相應的ServiceMethod逢唤,并放入緩存
  3. 創(chuàng)建動態(tài)代理(核心步驟)拉讯,以后每次調用接口的方法都會調用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;
    }

創(chuàng)建相應的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的請求,不允許出現(xiàn)@FormEncodedUrl或@MultiPart等注釋
     if (!hasBody) {
         if (isMultipart) {
                throw methodError(..);
         }
         if (isFormEncoded) {
               throw methodError(...);
         }
      }

     //處理參數(shù)的注解, 【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注釋則參數(shù)中至少有一個@Field注釋
       if (isFormEncoded && !gotField) {
             throw methodError(...);
       }
       //有@Multipart注釋著恩,參數(shù)中至少有一個@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. 注釋參數(shù)注釋

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,這個方法的主要作用是根據(jù)參數(shù)的注解進行不同的處理纵顾,參數(shù)的注解可以是以下幾種:

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

2.3 ServiceMethod.callAdapter.adapter

ServiceMethod.callAdapter是通過ExcecutorAdapterFactory.get()創(chuàng)建的一個匿名CallAdapter, 其實現(xiàn)為:


    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,后續(xù)走的就是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就是被動態(tài)代理的方法的參數(shù), 【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同樣實現(xiàn)了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++) {
            //不同參數(shù)的注釋有對應不同的handler, 以Query為例片挂,其handler是Query<T>
            handlers[p].apply(requestBuilder, args[p]);
        }

        return requestBuilder.build();
    }

這個方法的核心調用在于handlers[p].apply,不同的參數(shù)注釋幻林,對應這不同的ParameterHandler,以@Query為例贞盯,它所對應的ParammeterHandlerQuery<T>, Query<T>.apply的作用是將@Query所修飾的參數(shù)轉為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);
}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市整葡,隨后出現(xiàn)的幾起案子件余,更是在濱河造成了極大的恐慌,老刑警劉巖遭居,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件啼器,死亡現(xiàn)場離奇詭異,居然都是意外死亡俱萍,警方通過查閱死者的電腦和手機端壳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來枪蘑,“玉大人损谦,你說我怎么就攤上這事≡榔模” “怎么了照捡?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長话侧。 經(jīng)常有香客問我栗精,道長,這世上最難降的妖魔是什么瞻鹏? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任悲立,我火速辦了婚禮,結果婚禮上乙漓,老公的妹妹穿的比我還像新娘级历。我一直安慰自己,他們只是感情好叭披,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布寥殖。 她就那樣靜靜地躺著玩讳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪嚼贡。 梳的紋絲不亂的頭發(fā)上熏纯,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機與錄音粤策,去河邊找鬼樟澜。 笑死,一個胖子當著我的面吹牛叮盘,可吹牛的內容都是我干的秩贰。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼柔吼,長吁一口氣:“原來是場噩夢啊……” “哼毒费!你這毒婦竟也來了?” 一聲冷哼從身側響起愈魏,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤觅玻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后培漏,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體溪厘,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年牌柄,在試婚紗的時候發(fā)現(xiàn)自己被綠了畸悬。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡友鼻,死狀恐怖傻昙,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情彩扔,我是刑警寧澤妆档,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站虫碉,受9級特大地震影響贾惦,放射性物質發(fā)生泄漏。R本人自食惡果不足惜敦捧,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一须板、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧兢卵,春花似錦习瑰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽柠横。三九已至,卻和暖如春课兄,著一層夾襖步出監(jiān)牢的瞬間牍氛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工烟阐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留搬俊,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓蜒茄,卻偏偏與公主長得像唉擂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子扩淀,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內容

  • 寫在前面## 其實一直都沒有寫博客的習慣楔敌,借著這次公司內部分享的機會,開始寫博客驻谆,希望以后漸漸養(yǎng)成習慣。也方便以后...
    seph_von閱讀 886評論 0 1
  • 簡介 剛接觸Retrofit的時候庆聘,就寫了一篇簡單的使用介紹:Retrofit 2.0基本使用方法,算是對Retr...
    Whyn閱讀 2,844評論 4 24
  • Retrofit是squareup公司的開源力作伙判,和同屬squareup公司開源的OkHttp象对,一個負責網(wǎng)絡調度,...
    藍灰_q閱讀 41,519評論 23 281
  • 最近非常流行 Retrofit+RxJava+OkHttp 這一整套的網(wǎng)絡請求和異步操作的開源框架宴抚,從 Jake ...
    慌不要慌閱讀 1,974評論 1 7
  • Retrofit是一個Android網(wǎng)絡框架勒魔。是一個對OKHttp框架的簡單封裝。所以其內部實現(xiàn)原理實際上也是...
    龍城_閱讀 13,018評論 0 16