Android學(xué)習(xí)筆記——Retrofit2源碼淺分析

標(biāo)簽: Android

先看看這張圖


制圖簡(jiǎn)陋請(qǐng)多包涵

Retrofit

在學(xué)習(xí)Android的時(shí)候,我最熟悉的網(wǎng)絡(luò)請(qǐng)求框架莫過(guò)于OKHttp + Retrofit,反反復(fù)復(fù)用了很多次,個(gè)人感覺(jué)Retrofit的兼容和解耦做的太好了,這里想要試著分析一下Retrofit的源碼。

RESTful 原則

在我們使用Retrofit這個(gè)框架的時(shí)候,我們都需要后端的接口遵循RESTful原則,那什么是RESTful原則呢?
RESTful是Representational State Transfer的縮寫(xiě),翻譯過(guò)來(lái)就是“表現(xiàn)層狀態(tài)轉(zhuǎn)化”,他是基于HTTP協(xié)議的。

  1. 表現(xiàn)層:我們?cè)L問(wèn)服務(wù)器是為了“增、刪、改、查”服務(wù)器上面的資源(數(shù)據(jù)),資源的對(duì)應(yīng)的是一段文本(text)、一張圖片、一首歌曲或者一段音頻等,那么這里我們就說(shuō)這些資源的實(shí)體就是表現(xiàn)層。
  2. 狀態(tài)轉(zhuǎn)化:當(dāng)我們通過(guò)用“增、刪、改、查”的方式訪問(wèn)服務(wù)器的時(shí)候,服務(wù)器的資源實(shí)體可能會(huì)發(fā)生變化,對(duì)應(yīng)的狀態(tài)也會(huì)相映的變化,這就是狀態(tài)轉(zhuǎn)化
  3. 客戶端怎么進(jìn)行“增、刪、改、查”? 通過(guò)HTTP協(xié)議,具體就是用HTTP提供的GET、POST、DELETE、PUT的方式

Retrofit分層

我們導(dǎo)入Retrofit的時(shí)候,發(fā)現(xiàn)我們導(dǎo)入的包分為了三個(gè)部分:包裝OKHttp的Retrofit部分,兼容RxJava的部分和兼容Gson的部分。

一、包裝OKHttp

我們來(lái)看看這個(gè)部分的目錄:


image.png
  1. 首先這里有一個(gè)子目錄http,進(jìn)去看我們就可以發(fā)現(xiàn)里面定義了若干的關(guān)于http協(xié)議的注解(包括GET、POST、PUT和DELETE等)。
  2. 接下來(lái)就是Retrofit以及它包裝的網(wǎng)絡(luò)請(qǐng)求執(zhí)行器,請(qǐng)求的構(gòu)建器,響應(yīng)、轉(zhuǎn)化器、線程池和工具等等(以上沒(méi)按照順序)

二、兼容Gson

我們來(lái)看看這個(gè)部分的目錄:


image.png

這個(gè)部分很簡(jiǎn)單,只包含了三個(gè)部分:Gson轉(zhuǎn)換器的工廠、Gson請(qǐng)求體的轉(zhuǎn)換器和Gson響應(yīng)體的轉(zhuǎn)換器。

三、兼容RxJava

我們?cè)賮?lái)看看這個(gè)部分的目錄:


image.png

這個(gè)部分我們可以看到很多繼承自RxJava的Observable對(duì)象,都由RxJava2CallAdapter封裝,最后由它的工廠類(lèi)RxJava2CallAdapterFactory創(chuàng)建出來(lái)。

Retrofit 流程源碼分析

一般情況下我們使用Retrofit的代碼如下:

    mApiService = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(BASE_URL)
                .client(getClient())
                .build()
                .create(ApiService.class);

先是通過(guò)Retrofit的構(gòu)建器構(gòu)建一個(gè)Retrofit對(duì)象,前面的幾個(gè)鏈?zhǔn)秸{(diào)用都是為了支持其他三方庫(kù)的功能,我們直接去看Retrofit的構(gòu)建器的build()方法。

Retrofit.Builder.java

    //成員
    //當(dāng)前的平臺(tái)
    private final Platform platform;
    //網(wǎng)絡(luò)請(qǐng)求執(zhí)行器的工廠
    private @Nullable okhttp3.Call.Factory callFactory;
    private HttpUrl baseUrl;
    //可能會(huì)用到的轉(zhuǎn)換器工廠的集合
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    //可能會(huì)用到的適配器工廠的集合
    private final List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
    //可能會(huì)用到的線程池,默認(rèn)為null
    private @Nullable Executor callbackExecutor;
    //是否在調(diào)用create()方法時(shí),驗(yàn)證傳入的接口中的所有方法
    private boolean validateEagerly;
    
    .......
    
    
 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;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // 制作適配器的防御副本并添加默認(rèn)的呼叫適配器.
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      // 制作轉(zhuǎn)換器的防御副本.
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

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

我們可以看見(jiàn),這里和簡(jiǎn)單的構(gòu)建者模式?jīng)]什么區(qū)別,初始化需要用到的成員變量,然后再直接通過(guò)Retrofit的構(gòu)造函數(shù)創(chuàng)建一個(gè)實(shí)例對(duì)象,接下來(lái)我們?nèi)タ纯碿reate()方法。

public <T> T create(final Class<T> service) {
    //驗(yàn)證服務(wù)接口,服務(wù)接口必須是一個(gè)interface對(duì)象,且它沒(méi)有任何父接口
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    //通過(guò)動(dòng)態(tài)代理創(chuàng)建服務(wù)接口的實(shí)現(xiàn)類(lèi)
    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, @Nullable Object[] args)
              throws Throwable {
            // 如果是Object的方法,不做任何處理
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            //這個(gè)條件判斷在Retrofit3.2.0時(shí)一直為false,其他版本不知道
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            //這里通過(guò)服務(wù)接口的方法加載了一個(gè)ServiceMethod對(duì)象
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

在create()方法中,首先驗(yàn)證了我們傳入的服務(wù)接口類(lèi),然后通過(guò)動(dòng)態(tài)代理拿到方法和參數(shù),通過(guò)拿到的方法加載ServiceMethod對(duì)象,那么ServiceMethod是干什么的呢?注釋是這么說(shuō)的:將接口的方法調(diào)整為Http調(diào)用。我們先保留疑問(wèn),去看一下loadServiceMethod()方法做了什么:

 ServiceMethod<?, ?> loadServiceMethod(Method method) {
    //通過(guò)緩存拿到
    ServiceMethod<?, ?> result = serviceMethodCache.get(method);
    if (result != null) return result;
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        // 沒(méi)有緩存就通過(guò)構(gòu)建器構(gòu)建一個(gè)新的ServiceMethod
        result = new ServiceMethod.Builder<>(this, method).build();
        //緩存
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

這個(gè)方法的干的事情就是,判斷是否有緩存,有就直接返回,沒(méi)有就通過(guò)構(gòu)建器構(gòu)建一個(gè)新的ServiceMethod對(duì)象,這里的緩存是通過(guò)CurrentHashMap,鍵為方法,值為ServiceMethod對(duì)象。這里模擬第一次調(diào)用,沒(méi)有緩存,所以我們器看看ServiceMethod的構(gòu)建器:

   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();
      //通過(guò)響應(yīng)類(lèi)型
      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?");
      }
      //拿到轉(zhuǎn)換器
      responseConverter = createResponseConverter();

      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }

      if (httpMethod == null) {
        throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
      }

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

      if (relativeUrl == null && !gotUrl) {
        throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
      }
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError("Non-body HTTP method cannot contain @Body.");
      }
      if (isFormEncoded && !gotField) {
        throw methodError("Form-encoded method must contain at least one @Field.");
      }
      if (isMultipart && !gotPart) {
        throw methodError("Multipart method must contain at least one @Part.");
      }
      return new ServiceMethod<>(this);
    }
  1. 這里先拿到適配器,判斷適配器的響應(yīng)類(lèi)型是否合法
  2. 然后再拿到轉(zhuǎn)換器
  3. 循環(huán)解析——服務(wù)接口中被調(diào)用的方法上的注解
  4. 檢查各項(xiàng)數(shù)據(jù),new出ServiceMethod實(shí)例

下面我們依次分析這幾步:
第一步: 拿到適配器

private CallAdapter<T, R> createCallAdapter() {
      //獲取通用的返回類(lèi)型
      Type returnType = method.getGenericReturnType();
      //返回類(lèi)型通配符或者變量類(lèi)型
      if (Utils.hasUnresolvableType(returnType)) {
        throw methodError(
            "Method return type must not include a type variable or wildcard: %s", returnType);
      }
      //返回類(lèi)型不能是無(wú)返回類(lèi)型
      if (returnType == void.class) {
        throw methodError("Service methods cannot return void.");
      }
      //拿到傳入該方法的的注解
      Annotation[] annotations = method.getAnnotations();
      try {
        //noinspection unchecked
        return (CallAdapter<T, R>) 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);
      }
    }

這里先是判斷了服務(wù)接口中,被用戶調(diào)用的方法的返回類(lèi)型是否合法,合法之后拿到該方法的注解,轉(zhuǎn)入Retrofit中調(diào)用callAdapter()方法,下面我們?nèi)タ纯催@個(gè)方法:

public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
  }

這里直接就調(diào)用了nextCallAdapter()方法,我們繼續(xù)跟進(jìn):

 public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    // 判斷參數(shù)是否為空
    checkNotNull(returnType, "returnType == null");
    checkNotNull(annotations, "annotations == null");
    //找到與傳入?yún)?shù)值相等的變量在List中的索引
    int start = adapterFactories.indexOf(skipPast) + 1;
    //循環(huán)遍歷找到需要的適配器
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }
    ......
  }

這里進(jìn)行了參數(shù)的非空判斷,然后循環(huán)遍歷List,我們看向第十行這里調(diào)用了CallAdapter.Factory的實(shí)現(xiàn)類(lèi)(應(yīng)該是RxJava2CallAdapterFactory)的get()方法,這個(gè)方法會(huì)判斷調(diào)用的服務(wù)接口里面的方法的返回類(lèi)型,是否為Completable、Flowable、Maybe,Single、Obervable,之后會(huì)直接new出來(lái)需要的適配器(Adapter)。

我們?cè)谌タ纯吹诙剑耗玫睫D(zhuǎn)換器

  private Converter<ResponseBody, T> createResponseConverter() {
      //獲取方法的注解
      Annotation[] annotations = method.getAnnotations();
      try {
      //轉(zhuǎn)到Retrofit中去拿到轉(zhuǎn)換器
        return retrofit.responseBodyConverter(responseType, annotations);
      } catch (RuntimeException e) { // Wide exception range because factories are user code.
        throw methodError(e, "Unable to create converter for %s", responseType);
      }
    }

我們跟到Retrofit中去看看responseBodyConverter()方法怎么拿到轉(zhuǎn)換器的。

 public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations){
    return nextResponseBodyConverter(null, type, annotations);
  }

這里和拿到適配器的流程很像,也調(diào)用了一個(gè)nextxxx()方法。

public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
      @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
    //檢查參數(shù)是否為空
    checkNotNull(type, "type == null");
    checkNotNull(annotations, "annotations == null");

    //獲取參數(shù)的值獲取在List對(duì)應(yīng)的索引
    int start = converterFactories.indexOf(skipPast) + 1;
    //循環(huán)遍歷,找到轉(zhuǎn)換器
    for (int i = start, count = converterFactories.size(); i < count; i++) {
      Converter<ResponseBody, ?> converter =
          converterFactories.get(i).responseBodyConverter(type, annotations, this);
      if (converter != null) {
        //noinspection unchecked
        return (Converter<ResponseBody, T>) converter;
      }
    }
    ......
  }

這里可以看見(jiàn),拿到轉(zhuǎn)換器的過(guò)程和拿到適配器的過(guò)程十分相似,重點(diǎn)還是第12行的responseBodyConverter()。根據(jù)我們平時(shí)的用法,這個(gè)方法直接new出來(lái)了一個(gè)GsonResponseBodyConverter(Gson響應(yīng)體轉(zhuǎn)換器)。

我們?nèi)タ纯吹谌剑?解析注解

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

這里解析注解主要有分為兩類(lèi):

  • 1 通過(guò)parseHttpMethodAndPath()方法解析出網(wǎng)絡(luò)請(qǐng)求的方法URL相對(duì)路徑
  • 2 通過(guò)parseHeaders()方法解析出——需要添加的請(qǐng)求頭

這里具體的解析方式我們就不深究了。
接下來(lái)我們看看第四步:檢查各項(xiàng)數(shù)據(jù),拿到ParameterHandler,new出ServiceMethod實(shí)例。這里就不挖這些代碼了。
我們回到Retrofit的create()方法:

public <T> T create(final Class<T> service) {
    //驗(yàn)證服務(wù)接口,服務(wù)接口必須是一個(gè)interface對(duì)象,且它沒(méi)有任何父接口
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    //通過(guò)動(dòng)態(tài)代理創(chuàng)建服務(wù)接口的實(shí)現(xiàn)類(lèi)
    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, @Nullable Object[] args)
              throws Throwable {
            // 如果是Object的方法,不做任何處理
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            //這個(gè)條件判斷在Retrofit3.2.0時(shí)一直為false,其他版本不知道
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            //這里通過(guò)服務(wù)接口的方法加載了一個(gè)ServiceMethod對(duì)象
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

ServiceMethod的loadServiceMethod()加載完了,接下來(lái)就是直接new出OkHttpCall(網(wǎng)絡(luò)執(zhí)行器),并把OkHttpCall通過(guò)RxJava2CallAdapter的adapt()方法傳過(guò)去,接下里我們看看adapt()方法:

public Object adapt(Call<R> call) {
    Observable<Response<R>> responseObservable = isAsync
        ? new CallEnqueueObservable<>(call)
        : new CallExecuteObservable<>(call);

    Observable<?> observable;
    if (isResult) {
      observable = new ResultObservable<>(responseObservable);
    } else if (isBody) {
      observable = new BodyObservable<>(responseObservable);
    } else {
      observable = responseObservable;
    }

    if (scheduler != null) {
      observable = observable.subscribeOn(scheduler);
    }

    if (isFlowable) {
      return observable.toFlowable(BackpressureStrategy.LATEST);
    }
    if (isSingle) {
      return observable.singleOrError();
    }
    if (isMaybe) {
      return observable.singleElement();
    }
    if (isCompletable) {
      return observable.ignoreElements();
    }
    return observable;
  }

首先通過(guò)服務(wù)接口中的方法的返回類(lèi)型中的泛型,判斷被觀察者的種類(lèi)(一般情況下我們都是responseObservable),然后通過(guò)服務(wù)接口中的方法的返回類(lèi)型判斷是Flowable、Single、Maybe或者就是Observable。

到這里我們的Retrofit一套簡(jiǎn)單流程就基本就分析完了,當(dāng)然這里是淺分析,當(dāng)然還有頗多存疑。

 mApiService = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(BASE_URL)
                .client(getClient())
                .build()
                .create(ApiService.class);

前面分析總結(jié):create()中使用了動(dòng)態(tài)代理,在實(shí)際調(diào)用服務(wù)接口中的方法時(shí),把方法和參數(shù)傳給了ServiceMethod,ServiceMethod通過(guò)構(gòu)建者模式創(chuàng)建,在build()方法中通過(guò)Retrofit拿到了適配器和轉(zhuǎn)換器,并且對(duì)方法的注解和參數(shù)進(jìn)行了解析,可以說(shuō)ServiceMethod做了很多事情,最后ServiceMethod通過(guò)調(diào)用它拿到的適配器,進(jìn)過(guò)判斷,返回網(wǎng)絡(luò)請(qǐng)求中的被觀察者。

有些童鞋可能會(huì)想,網(wǎng)絡(luò)請(qǐng)求到底是在什么時(shí)候執(zhí)行的呢?
通過(guò)我們拿到的被觀察者,訂閱我們自己寫(xiě)的觀察者時(shí),進(jìn)行的網(wǎng)絡(luò)請(qǐng)求。具體的,RxJava的訂閱方法subscrib()最終會(huì)調(diào)用subscribeActual()這個(gè)方法,這個(gè)方法是個(gè)抽象方法,在Retrofit的取得適配器中的adapt()方法(我們上面分析過(guò)這個(gè)方法)會(huì)new出新的被觀察者,這個(gè)新的觀察者會(huì)覆寫(xiě)subscribeActual()這個(gè)方法,通過(guò)傳入的網(wǎng)絡(luò)執(zhí)行器發(fā)起網(wǎng)絡(luò)請(qǐng)求。

本片文章謝謝童鞋們的觀看。

本人Andorid小白,水平有限,如果有不對(duì)的地方,希望大家提點(diǎn)一下我。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 注解式的框架非?;?,注解以其輕量,簡(jiǎn)潔等特性被人們所喜愛(ài)者,關(guān)鍵是它解藕。網(wǎng)絡(luò)請(qǐng)求的框架非常多,比較受歡迎的...
    薩達(dá)哈魯醬閱讀 627評(píng)論 0 5
  • 本文將順著構(gòu)建請(qǐng)求對(duì)象->構(gòu)建請(qǐng)求接口->發(fā)起同步/異步請(qǐng)求的流程,分析Retrofit是如何實(shí)現(xiàn)的。 開(kāi)始之前,...
    zhuhf閱讀 1,684評(píng)論 0 10
  • 安卓開(kāi)發(fā)領(lǐng)域中,很多重要的問(wèn)題都有很好的開(kāi)源解決方案,例如Square公司提供網(wǎng)絡(luò)請(qǐng)求 OkHttp , Retr...
    aaron688閱讀 1,993評(píng)論 1 20
  • Retrofit 是目前作為網(wǎng)絡(luò)請(qǐng)求的主流框架,使用起來(lái)很方便,僅需在接口中定義方法,打上注解,而且和 Rxjav...
    Kip_Salens閱讀 684評(píng)論 0 3
  • 今天下午我去上書(shū)法課,因此,我非常開(kāi)心,因?yàn)槲矣帜芤?jiàn)到我書(shū)法老師和同學(xué)們了。 到了書(shū)法班之后,我迫...
    豪好學(xué)習(xí)天天向上閱讀 145評(píng)論 0 0

友情鏈接更多精彩內(nèi)容