Retrofit原理淺析

Retrofit原理淺析

做Android也有幾年了,各種Android http類庫(kù)也用過(guò)不少,自己的做過(guò)的項(xiàng)目中也一直在嘗試怎么封裝讓API接口定義和API使用者解耦,但一直感覺(jué)沒(méi)有可以讓人滿意的框架,直到無(wú)意中在網(wǎng)上看到了JakeWharton大神的Retrofit這個(gè)類庫(kù)。不得不說(shuō),真心是炒雞解耦。

下面來(lái)簡(jiǎn)單分析一下這個(gè)Retrofit2的使用和實(shí)現(xiàn)。

類庫(kù)使用示例

首先定義請(qǐng)求接口

public interface GitHub {
    @GET("/repos/{owner}/{repo}/contributors")
    Call<List<Contributor>> contributors(
        @Path("owner") String owner,
        @Path("repo") String repo);
  }

然后通過(guò)Retrofit生成一個(gè)剛才定義的接口的實(shí)現(xiàn)類,使用的是動(dòng)態(tài)代理。

// Create a very simple REST adapter which points the GitHub API.
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl(API_URL)
    .addConverterFactory(GsonConverterFactory.create())
    .build();

// Create an instance of our GitHub API interface.
GitHub github = retrofit.create(GitHub.class);

之后就可以使用接口進(jìn)行請(qǐng)求了

// Create a call instance for looking up Retrofit contributors.
Call<List<Contributor>> call = github.contributors("square", "retrofit");

類庫(kù)原理解析

注解

Retrofit使用注解+java接口來(lái)定義后臺(tái)服務(wù)API接口

注解主要分為 方法注解 和 參數(shù)注解

注解 類型 作用
@GET 方法注解 表明HTTP請(qǐng)求方法為GET,(可選)注解的value屬性用來(lái)設(shè)置相對(duì)/絕對(duì)url
@POST 方法注解 表明HTTP請(qǐng)求方法為POST,(可選)注解的value屬性用來(lái)設(shè)置相對(duì)/絕對(duì)url
@PUT 方法注解 表明HTTP請(qǐng)求方法為PUT,(可選)注解的value屬性用來(lái)設(shè)置相對(duì)/絕對(duì)url
@DELETE 方法注解 表明http請(qǐng)求方法為DELETE,(可選)注解的value屬性用來(lái)設(shè)置相對(duì)/絕對(duì)url
@PATCH 方法注解 表明HTTP請(qǐng)求方法為PATCH,(可選)注解的value屬性用來(lái)設(shè)置相對(duì)/絕對(duì)url
@HEAD 方法注解 表明HTTP請(qǐng)求方法為HEAD,(可選)注解的value屬性用來(lái)設(shè)置相對(duì)/絕對(duì)url
@OPTIONS 方法注解 表明HTTP請(qǐng)求方法為OPTIONS,(可選)注解的value屬性用來(lái)設(shè)置相對(duì)/絕對(duì)url
@HTTP 方法注解 通過(guò)@HTTP注解指定http協(xié)議的請(qǐng)求方法,是否允許body,(可選)注解的value屬性用來(lái)設(shè)置相對(duì)/絕對(duì)url
@FormUrlEncoded 方法注解 表明發(fā)起HTTP請(qǐng)求的RequestBody是form表單方式
@Multipart 方法注解 表明發(fā)起HTTP請(qǐng)求的RequestBody是Multipar方式
@Headers 方法注解 使用注解的value值數(shù)組作為HTTP請(qǐng)求的頭,用于一些固定的Header參數(shù)
@Streaming 方法注解 用于需要直接返回流的函數(shù)
@Url 參數(shù)注解 HTTP請(qǐng)求的url路徑(相對(duì)/絕對(duì)),可以包含{path_holder},如:http://xxx.com/{user_holder}/detail
@Path 參數(shù)注解 用于動(dòng)態(tài)替換URL路徑中的path_holder
@Body 參數(shù)注解 表明此參數(shù)用作HTTP請(qǐng)求的body
@Field 參數(shù)注解 表明此參數(shù)用作HTTP請(qǐng)求的form表單參數(shù),key為注解的value值
@FieldMap 參數(shù)注解 以map形式傳入的form表單參數(shù)
@Header 參數(shù)注解 表明此參數(shù)用作HTTP請(qǐng)求的header,key為注解的value值
@HeaderMap 參數(shù)注解 以map形式傳入的多個(gè)header鍵值對(duì)
@Part 參數(shù)注解 表明參數(shù)為Http的multipart參數(shù)之一
@PartMap 參數(shù)注解 以map形式傳入的multipart參數(shù)表
@Query 參數(shù)注解 GET方法的query參數(shù),用于拼接完整請(qǐng)求路徑
@QueryMap 參數(shù)注解 以map傳入的GET方法的query參數(shù),用于拼接完整請(qǐng)求路徑

生成動(dòng)態(tài)代理實(shí)例

Retrofit使用的關(guān)鍵一步就是Retrofit.create函數(shù)創(chuàng)建接口動(dòng)態(tài)代理的示例,代碼如下

@SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
  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);
            }
            ServiceMethod serviceMethod = loadServiceMethod(method);
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

可以看到是為接口的每個(gè)method創(chuàng)建了一個(gè)對(duì)應(yīng)的ServiceMethod,并使用這個(gè)ServiceMethod對(duì)象創(chuàng)建OkHttpCall,并使用ServiceMethod實(shí)例的callAdapter來(lái)調(diào)用okhttpCall并返回結(jié)果。

調(diào)用流程

通過(guò)上面代碼可以看到調(diào)用關(guān)鍵的就是三步:

  • 1 加載對(duì)應(yīng)method的ServiceMethod實(shí)例
  • 2 使用ServiceMethod實(shí)例和方法調(diào)用參數(shù)創(chuàng)建OkHttpCall
  • 3 調(diào)用serviceMethod.callAdapter.adapt(okHttpCall)來(lái)產(chǎn)生method所定義的返回(Call<T>或者其他自定義CallAdapter支持的返回)

第一步、加載對(duì)應(yīng)method的ServiceMethod實(shí)例

ServiceMethod中有以下四個(gè)變量比較重要

final okhttp3.Call.Factory callFactory;
final CallAdapter<?> callAdapter;
private final Converter<ResponseBody, T> responseConverter;
private final ParameterHandler<?>[] parameterHandlers;
  • callFactory是用來(lái)創(chuàng)建真正要執(zhí)行的okhttp3.Call的工廠類,可以Retrofit.Builder中設(shè)置,如果不設(shè)置,默認(rèn)會(huì)new一個(gè)OkHttpClient作為callFactory
  • callAdapter是用來(lái)最終處理OkHttpCall實(shí)例并返回接口Method所定義的返回
  • responseConverter 用來(lái)將Http請(qǐng)求的結(jié)果轉(zhuǎn)換成接口Method所定義的結(jié)果(return或者Callback<T>中的T)
  • parameterHandlers 根據(jù)接口Method參數(shù)的注解所生成的參數(shù)處理Handler數(shù)組

然后我們來(lái)看Retrofit.loadServiceMethod方法

    ServiceMethod loadServiceMethod(Method method) {
    ServiceMethod result;
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = new ServiceMethod.Builder(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

可以看到此處先檢查serviceMethodCache是否有該method對(duì)應(yīng)的ServiceMethod實(shí)例緩存,如果沒(méi)有,則創(chuàng)建一個(gè)該method對(duì)應(yīng)的ServiceMethod實(shí)例并保存到緩存中。
ServiceMethod的創(chuàng)建使用的是建造者模式。

在ServiceMethod.Builder的build方法中,通過(guò)解析傳入的method的方法定義(參數(shù)類型,返回類型,參數(shù)注解,方法注解)生成對(duì)應(yīng)的callAdapter,responseConverter,parameterHandlers及其他一些創(chuàng)建請(qǐng)求需要用到的信息。

    public ServiceMethod build() {
      callAdapter = createCallAdapter();
      ......檢查返回結(jié)果類型......
      responseConverter = createResponseConverter();
      //生成方法注解的處理器
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
      .....方法與注解合法性檢查.....
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
        .....注解合法性檢查....
        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }
      ......方法與注解合法性檢查......
      return new ServiceMethod<>(this);
    }

此處在ServiceMethod.Builder.build()過(guò)程在生成過(guò)程中還會(huì)對(duì)method的定義做合法性檢查,如:http方法是get就不允許方法參數(shù)中有body類型的參數(shù);方法為post則必須有參數(shù)為Body類型。

第二步、使用ServiceMethod實(shí)例和方法調(diào)用參數(shù)創(chuàng)建OkHttpCall

獲取到method對(duì)應(yīng)的ServiceMethod實(shí)例后,會(huì)使用該ServiceMethod實(shí)例和方法調(diào)用的參數(shù)Object... args生成一個(gè)OkHttpCall。而OkHttpCall實(shí)際上是okhttp3.Call的一個(gè)包裝類,實(shí)際調(diào)用OkHttpCall的相關(guān)執(zhí)行方法時(shí)最終是調(diào)用OkHttpCall內(nèi)部用ServiceMethod.callFactory創(chuàng)建的okhttp3.Call來(lái)執(zhí)行網(wǎng)絡(luò)請(qǐng)求。

第三步、調(diào)用serviceMethod.callAdapter.adapt(okHttpCall)來(lái)產(chǎn)生method所定義的返回

Retrofit2默認(rèn)支持的返回是返回一個(gè)Call<T>,利用此Call<T>實(shí)例可執(zhí)行

Response<T> result = call.execute();//同步執(zhí)行

//異步執(zhí)行
call.enqueue(new Callback(){
    public void onResponse(Call<T> call, Response<T> response){
        //TODO 
    }
    public void onFailure(Call<T> call, Throwable t){
        //TODO 
    }   
});

其中在Android平臺(tái)Retrofit2會(huì)自動(dòng)使用主線程handler構(gòu)造一個(gè)ExecutorCallAdapterFactory,調(diào)用enqueue(Callback),callback回調(diào)會(huì)在主線程中回調(diào)

另外在Retrofit的擴(kuò)展Adapter中還提供了RxJavaCallAdapterFactory,Java8CallAdapterFactory,GuavaCallAdapterFactory

以RxJavaCallAdapterFactory為例,RxJavaCallAdapterFactory創(chuàng)建的callAdapter在執(zhí)行adapt時(shí)將OkHttpCall包裝一個(gè)Rx的Observable,在Observable被subscribe時(shí)才會(huì)真正的執(zhí)行http請(qǐng)求。

寫在最后

其實(shí)Retrofit已經(jīng)不是一個(gè)新庫(kù)了,目前版本也是第二個(gè)大版本了,本身Retrofit已經(jīng)讓http請(qǐng)求解耦很好了,再加上與Rx的配合,就真的是靈活到不行。我也是從去年下半年才開(kāi)始關(guān)注和研究Retrofit的實(shí)現(xiàn),此文章也只是一個(gè)淺析,希望能對(duì)各位開(kāi)發(fā)同仁有些幫助吧。

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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