框架源碼 — 可能會有趣一點地簡析學習 Retrofit

看過很多篇 Retrofit 的源碼分析文章,但是別人一問起來總是講不清楚到底 Retrofit 是怎么個流程,所以還是得自己親自去看看源碼,一步一步的分析。果然只有親自動手實踐,才有自己的收獲。
告誡自己,慢慢來,會很快。

目錄

Retrofit 簡介

Retrofit 源碼開頭的解釋

* Retrofit adapts a Java interface to HTTP calls by using annotations on the declared methods to
* define how requests are made. Create instances using {@linkplain Builder
* the builder} and pass your interface to {@link #create} to generate an implementation.

Retrofit 利用方法上的注解將接口轉化成一個 HTTP 請求。

簡單知道是什么了之后,我們對此提出疑問:

  • 如何將接口轉換為網(wǎng)絡請求?
  • 誰去進行網(wǎng)絡請求?

接下來我們將從 Retrofit 的使用作為入口分析。

Retrofit 分析

具體使用

首先建立 API 接口類:

interface GankApi {
    String host = "http://gank.io/api/data/";
    @GET("Android/10/{page}")
    Call<Android> getAndroid(@Path("page") int page);
}

// 創(chuàng)建 Retrofit 實例
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl(GankApi.host)
    .addConverterFactory(GsonConverterFactory.create())
    .build();

// 生成接口實現(xiàn)類
GankApi gankApi = retrofit.create(GankApi.class);

// 調(diào)用接口定義的請求方法,并且返回 Call 對象
Call<Android> call = gankApi.getAndroid(1);

// 調(diào)用 Call 對象的異步執(zhí)行方法
call.enqueue(Callback callback)

簡單的使用就是這樣的流程?,F(xiàn)在我們開始層層剖析。

工具箱:Retrofit.Builder()

private Platform platform;
private okhttp3.Call.Factory callFactory;
private HttpUrl baseUrl;
private List<Converter.Factory> converterFactories = new ArrayList<>();
private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
private Executor callbackExecutor;
private boolean validateEagerly;

創(chuàng)建 Retrofit 的實例,進行一些配置,這里我們不用多說。但是有一個參數(shù)必須得講講。

  • Platform

在構建 Retrofit 的時候,會對當前使用平臺進行判斷,Java8,Android,iOS。

我們看看 Android 平臺的代碼:

static class Android extends Platform {
  @Override public Executor defaultCallbackExecutor() {
    return new MainThreadExecutor();
  }

  @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
    return new ExecutorCallAdapterFactory(callbackExecutor);
  }

  static class MainThreadExecutor implements Executor {
    private final Handler handler = new Handler(Looper.getMainLooper());

    @Override public void execute(Runnable r) {
      handler.post(r);
    }
  }
}

從代碼中我們得知兩點:

  1. 在 Android 里我們默認使用的 CallAdapter 是 ExecutorCallAdapterFactory() 它會返回的是 Call.class。關于 ExecutorCallAdapterFactory() 我們稍后再說,你先知道這是 Android 默認 CallAdapter 就好。
  2. 默認的 Callback 是在主線程。

外殼:Create()

// 生成接口實現(xiàn)類
GankApi gankApi = retrofit.create(GankApi.class);

我在源碼里寫好了注釋:

public <T> T create(final Class<T> service) {
    // 檢查傳入的類是否為接口并且無繼承
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    // 重點是這里
    // 首先會返回一個利用代理實現(xiàn)的 GankApi 對象
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          // 我們調(diào)用該對象的方法都會進入到這里
          @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);
            }
            // 解析方法 這里用到了注解(Runtime)這里我們標記下(A)稍后來看看里面具體實現(xiàn)
            ServiceMethod serviceMethod = loadServiceMethod(method);
            // 將剛剛解析完畢包裝后的具體方法封裝成 OkHttpCall ,你可以在該實現(xiàn)類找到 okhttp 請求所需要的參數(shù)
            // 所以它是用來跟 okhttp 對接的。
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            // 將以上我們封裝好的 call 返回給上層,這個時候我們就可以執(zhí)行 call 的同步方法或者異步進行請求。
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

切合我們實際運用來看看順序:

GankApi gankApi = retrofit.create(GankApi.class);—->
return (T) Proxy.newProxyInstance(...){...}—->
Call<Android> call = gankApi.getAndroid(1); —->
public Object invoke(...){...} 調(diào)用代理類的invoke()。

直到這里我們已經(jīng)宏觀地了解 Retrofit 是怎樣的一個流程。
達成 初窺門徑 成就。

千萬別驕傲,為了以后走的更遠更穩(wěn),我們得好好筑基,上面我們用到的是動態(tài)代理,強烈建議認真閱讀兩篇文章。

結構:ServiceMethod

Retrofit 有一個雙鏈表用來緩存方法
private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();

  ServiceMethod loadServiceMethod(Method method) {
  ServiceMethod result;
  synchronized (serviceMethodCache) {
      // 從緩存中獲取該方法
    result = serviceMethodCache.get(method);
    if (result == null) {
        // 沒有就進行創(chuàng)建并且存入鏈表緩存
      result = new ServiceMethod.Builder(this, method).build();
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}

我們發(fā)現(xiàn)主要的方法是 new ServiceMethod.Builder(this, method).build(); ,所以接下來我們深入看看如何 解析注解 以及 構建請求方法 。

  • 初始化一些參數(shù)
public Builder(Retrofit retrofit, Method method) {
  this.retrofit = retrofit;
  this.method = method;
  this.methodAnnotations = method.getAnnotations();
  this.parameterTypes = method.getGenericParameterTypes();
  this.parameterAnnotationsArray = method.getParameterAnnotations();
}
  • build()

這里的源碼很長,做了很多異常處理,我截取重點來分析下。

callAdapter = createCallAdapter();
responseConverter = createResponseConverter();

一個是用來發(fā)送請求的 client ,一個是結果的轉換器(Gson,F(xiàn)astJson ...)之類,后面我們再講這個。
上層配置就是當我們調(diào)用 Retrofit 的 addConverterFactory()addCallAdapterFactory(),內(nèi)部會自動使用我們定義的組件。

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

在這里可以看到遍歷我們使用方法的注解,并且解析他們。parseMethodAnnotation() 內(nèi)部就是解析好 HTTP 的請求方式。

為了篇幅大小,可以在 源碼 里看看具體的操作。

同時也可以看看 http 包下注解用到的接口,你會發(fā)現(xiàn) @Retention(RUNTIME) 所以,從這里我們就可以明白,Retrofit 是在在運行期通過反射訪問到這些注解的。

  • return Call

請求方法參數(shù),請求客戶端,返回值轉換,我們都定義好了之后,便完成最后一步,構建好適合請求客戶端的請求方法,Retrofit 默認的是 okhttpCall 。

OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);

最后將 call 返回給上層,用戶調(diào)用方法進行請求。

  • 總結
/** Adapts an invocation of an interface method into an HTTP call. */

ServiceMethod 類開頭注釋已經(jīng)很清楚的說明了作用,將接口方法改變成一個 HTTP call 。它對于 Retrofit 是很重要的存在,整個槍支內(nèi)部都是由它來支撐起來。

子彈:xxxFactory()

Retrofit 給我們最大的便利就是自身框架優(yōu)雅的設計,只需要很小的改動,便可以優(yōu)雅的適應不同的需求。所以很需要我們再補充點額外知識,了解什么是適配器模式,然后回到這里看看 Retrofit 是如何應用的。

在構建 ServiceMethod 對象的時候,有三個方法可以單獨說說

  1. build()createCallAdapter() —-> retrofit.callAdapter()
  2. 解析接口方法內(nèi)注解時parseParameterAnnotation()調(diào)用到的retrofit.requestBodyConverter()
  3. build()createResponseConverter() —-> retrofit.responseBodyConverter()

callAdapter()

最終會調(diào)用到 nextCallAdapter() 該方法主要是從 callAdapterFactories 中獲取新的 CallAdapter,它會跳過 skipPast,以及 skipPast 之前的 Factory,然后找到與 returnType 和 annotations 都匹配的 CallAdapterFactory 。

requestBodyConverter() & responseBodyConverter()

最終會調(diào)用到 nextRequestBodyConverter()/nextResponseBodyConverter利用 converterFactories 創(chuàng)建一個與 RequestBody/ResponseBody 對應的 Converter 對象。

所以在這里我們就可以裝填我們需要的子彈類型了。

進入實戰(zhàn),為我們的 Retrofit 添加 RxJava 和 Gson。

  • Rxjava:
23:33:50.jpg

adapter-rxjava 我們重點看 RxJavaCallAdapterFactory 即可,它是實現(xiàn)了 CallAdapter.Factory 并在對應方法里將 Call 包裝成 Observable.class 返回。
然后給 Retrofit 對象加上 .addCallAdapterFactory(RxJavaCallAdapterFactory.create()),這樣我們才可以優(yōu)雅的使用 Retrofit + RxJava 。

  • Gson:
23:34:46.jpg

我相信通過類名我們就可以知道每個類是用來做什么的,我在這里太過深入到具體實現(xiàn)反而一葉障目,
如果我們需要自定義數(shù)據(jù)轉換格式,也是同樣這樣做。
繼承 Converter.Factory 類作為適配類,同時創(chuàng)建兩個實現(xiàn) Converter 的類包裝請求和響應的數(shù)據(jù)形式。

開槍打靶: Call.enqueue()

<div class="tip">
注意:我這里只列舉一個默認狀態(tài)下的情況
</div>

?還記得我工具箱里我們提到的 ExecutorCallbackCall 嗎?
這里的 Call 是對應我們選擇的 call ,而此時是默認的 ExecutorCallbackCall 。如果還要問我為什么,請去看看 工具箱:Retrofit.Builder() 里 Android 平臺的源碼。

static final class ExecutorCallbackCall<T> implements Call<T> {
  final Executor callbackExecutor;
  final Call<T> delegate;

  ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
    this.callbackExecutor = callbackExecutor;
    this.delegate = delegate;
  }

  @Override public void enqueue(final Callback<T> callback) {
    if (callback == null) throw new NullPointerException("callback == null");

    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()) {
              // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
              callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
            } else {
              callback.onResponse(ExecutorCallbackCall.this, response);
            }
          }
        });
      }

      // 省略
}

這里的 delegate 對應的就是 okhttp 的 call ,不禁有疑問了,這里調(diào)用的是異步請求,但是我們的回調(diào)是怎么回到主線程的呢?

帶著疑問我們來看看。
首先回調(diào)是在 callbackExecutor.execute() 我們從這里入手。
我們發(fā)現(xiàn)在 Retrofit 的 build() 方法里:

Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
  callbackExecutor = platform.defaultCallbackExecutor();
}

平臺默認的回調(diào)調(diào)度器,連忙回到工具箱看看:

static class Android extends Platform {
  @Override public Executor defaultCallbackExecutor() {
    return new MainThreadExecutor();
  }

  @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
    return new ExecutorCallAdapterFactory(callbackExecutor);
  }

  static class MainThreadExecutor implements Executor {
    private final Handler handler = new Handler(Looper.getMainLooper());

    @Override public void execute(Runnable r) {
      handler.post(r);
    }
  }
}

我們發(fā)現(xiàn),Android 默認的調(diào)度器是主線程的 Handler ,execute()方法也只是 mainHandler.post()

所以這下就可以解決我們的疑問了。

callbackExecutor.execute(new Runnable() {
  @Override public void run() {
    if (delegate.isCanceled()) {
      // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
      callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
    } else {
      callback.onResponse(ExecutorCallbackCall.this, response);
    }
  }
});

這段代碼我們就可以改寫為:

mainHandler.post(new Runnable() {
  @Override public void run() {
    if (delegate.isCanceled()) {
      // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
      callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
    } else {
      callback.onResponse(ExecutorCallbackCall.this, response);
    }
  }
});

如果看到這,還不理解為什么那就得好好補補 handler 的知識啦!

我這里推薦 melo 寫的這篇,風趣易懂 帶著這篇去通關所有Handler的提問

最后放上兩張開源社區(qū)畫的流程圖,我覺得特別清晰:

23:27:17.jpg
23:28:01.jpg

以上?!ā?`)

參考

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

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

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