Retrofit原理淺析

Retrofit原理淺析

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

下面來簡單分析一下這個Retrofit2的使用和實現(xiàn)。

類庫使用示例

首先定義請求接口

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

然后通過Retrofit生成一個剛才定義的接口的實現(xiàn)類,使用的是動態(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);

之后就可以使用接口進行請求了

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

類庫原理解析

注解

Retrofit使用注解+java接口來定義后臺服務API接口

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

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

生成動態(tài)代理實例

Retrofit使用的關鍵一步就是Retrofit.create函數(shù)創(chuà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);
          }
        });
  }

可以看到是為接口的每個method創(chuàng)建了一個對應的ServiceMethod,并使用這個ServiceMethod對象創(chuàng)建OkHttpCall,并使用ServiceMethod實例的callAdapter來調用okhttpCall并返回結果。

調用流程

通過上面代碼可以看到調用關鍵的就是三步:

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

第一步、加載對應method的ServiceMethod實例

ServiceMethod中有以下四個變量比較重要

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

然后我們來看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對應的ServiceMethod實例緩存,如果沒有,則創(chuàng)建一個該method對應的ServiceMethod實例并保存到緩存中。
ServiceMethod的創(chuàng)建使用的是建造者模式。

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

    public ServiceMethod build() {
      callAdapter = createCallAdapter();
      ......檢查返回結果類型......
      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()過程在生成過程中還會對method的定義做合法性檢查,如:http方法是get就不允許方法參數(shù)中有body類型的參數(shù);方法為post則必須有參數(shù)為Body類型。

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

獲取到method對應的ServiceMethod實例后,會使用該ServiceMethod實例和方法調用的參數(shù)Object... args生成一個OkHttpCall。而OkHttpCall實際上是okhttp3.Call的一個包裝類,實際調用OkHttpCall的相關執(zhí)行方法時最終是調用OkHttpCall內部用ServiceMethod.callFactory創(chuàng)建的okhttp3.Call來執(zhí)行網(wǎng)絡請求。

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

Retrofit2默認支持的返回是返回一個Call<T>,利用此Call<T>實例可執(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平臺Retrofit2會自動使用主線程handler構造一個ExecutorCallAdapterFactory,調用enqueue(Callback),callback回調會在主線程中回調

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

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

寫在最后

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

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

相關閱讀更多精彩內容

友情鏈接更多精彩內容