Retrofit2分析

Retrofit2已經面世好久了,有不少好的文章分析過,這篇文章我只想記錄本身閱讀Retrofit 2.3.0源碼後的分析過程,如何閱讀源碼以及分析我以爲是最重要的。java

目錄

  1. Retrofit2 使用
    • 使用步驟
  2. 源碼分析
    • 構建 Retrofit 對象
    • 動態代理
    • 調用流程
  3. 自定義 ConverterFactory
  4. 總結

1、Retrofit2使用

1. 使用步驟

以前作過Gank的客戶端,所以直接用Gank網站的請求了。git

構建retrofit對象:github

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://gank.io")
    .build();
複製代碼

定義請求interface, 能夠把它內部的每個接口方法看作是一個封裝了請求url及參數的方法,其返回值是可執行請求的對象,而在 Retrofit 中默認是 Call 可執行對象,也就是說 call 調用某個方法,如 enqueue 就能夠異步執行請求。設計模式

public interface ApiService {

    @GET("api/data/{type}/{size}/{page}")
    Call<ResponseBody> getArticles(@Path("type") String type, @Path("size") int size,
    		@Path("page") int page);

}
複製代碼

生成代理對象:api

ApiService apiService = retrofit.create(ApiService.class);
複製代碼

獲取 call 這個可執行請求的對象,並enqueue異步執行請求:緩存

Call<ResponseBody> call = apiService.getArticles("Android",10, 1);
call.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        try {
            Log.d(TAG,response.body().string());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
    }
});
複製代碼

call.enqueue會執行真正的請求,而且內部會切換到主線程回調到 onResponse 方法內可拿到最終結果。bash

附上 官方Retrofit使用文檔數據結構

Retrofit本質上是對okhttp3的封裝,其最大的優點就是解耦作的很是好,接下來就從源碼角度分析下。異步

2、源碼分析

1. 構建Retrofit對象

構建Retrofit用到了 Builder 模式,很適合自定義一些東西。先走到 baseUrl 方法內看下:ide

public Builder baseUrl(String baseUrl) {
  checkNotNull(baseUrl, "baseUrl == null");
  HttpUrl httpUrl = HttpUrl.parse(baseUrl);
  if (httpUrl == null) 
    throw new IllegalArgumentException("Illegal URL: " + baseUrl);
  }
  return baseUrl(httpUrl);
}
複製代碼

將字符串類型的url解析成 HttpUrl對象,這個對象能夠理解爲拆分了url的協議、域名、端口、路徑等變量並保存在了一個對象中,須要哪一個部分就能夠隨時拿出,最後保存下 httpUrl 變量。

最簡單的就是設置一個地址,而後直接調用 build 方法:

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();
  }
  // Make a defensive copy of the adapters and add the default Call adapter.
  List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
  adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
  // Make a defensive copy of the converters.
  List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
  return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
      callbackExecutor, validateEagerly);
}
複製代碼

若是不在構建Retrofit時設置 client 那麼就會默認建立一個 OkHttpClient, 前面已經說過 Retrofit 是對 okhttp 的封裝而已,本質上仍是okhttp 進行請求,而如今只是分析 Retrofit, 所以不打算深刻OkHttpClient, 可是根據它的接口類型也能判斷是一個 生產okhttp中的Call對象的工廠, 而Call對象就是可執行任務的對象, Retrofit 中也有 Call 致使有點混亂。。須要好好辨別下。

若不設置 callbackExecutor 也會經過 platform.defaultCallbackExecutor() 建立一個默認的,經過看源碼發現Retrofit也就支持兩個平臺,一個是Java8,一個就是Android :

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

    @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor
    			callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
      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);
      }
    }
  }
複製代碼

它會返回一個 MainThreadExecutor 對象,而它的實現很是簡單,就是經過Handler將線程切換到主線程,記住這個 callbackExecutor , 它會在某個地方執行 execute 方法這個時候會切到主線程進行回調。

adapterFactories列表對象能夠把它理解爲 生產 adapter 的工廠,而 adapter 從名字上來看是適配器,它調用其中的 adapt 方法返回的就是可執行對象!在 Retrofit 裏默認的實現就是 Call 對象,而且因爲 Retrofit 的神奇解耦,它能夠自定義任何CallAdapter 和 Factory,最有名的就是RxJava,其返回的可執行對象變成了Observable而已 ,列表表示能夠添加多個 callAdapter工廠, 根據你寫的接口方法的返回值判斷選擇哪一個適配器。

默認狀況下調用 platform.defaultCallbackExecutor() 建立 ExecutorCallAdapterFactory ,這個類很關鍵,工廠顧名思義就是生產CallAdapter的,其中的 get 方法返回了一個 CallAdapter 對象,這個對象會在某個關鍵時刻調用 adapt 從而返回 Call 對象,這個調用流程以後再詳解。

converterFactories 轉換器工廠,其套路和 adapterFactories 很是相似,它的做用就是將請求後返回的數據轉換成你想要的數據結構,應用最廣的應該是 GsonConverterFactory , 能夠將結果用 Gson 轉換成自定義好的數據結構。其默認實現是 BuiltInConverters ,這個默認的工廠返回的 Converter 基本沒作什麼事情,基本只是把 okhttp 返回給 Retrofit 的 ResponseBody 數據結構返回出去。所以暫時不須要太過在乎。

最後以這些對象做爲參數傳給 Retrofit 構造一個 Retrofit 實例。總結下關鍵的幾個對象:將 url 解析成 HttpUrl 對象並保存供之後使用;在不自定義的狀況下默認建立 callFactory, 這個 OkHttpClient 的實例,最終會經過這個工廠生成 okhttp 中的 call 對象來執行真正的請求;callbackExecutor默認實現是 MainThreadExecutor 是用來最後切換到主線程用的;adapterFactories用來生產adapter,默認是 ExecutorCallAdapterFactory,最後會返回 Call 對象;converterFactories默認實現是 BuiltInConverters, 基本上就是將 okhttp 返回的 ResponseBody 返回出去。

2. 動態代理

動態代理這個能夠說是 Retrofit 最精華的地方了。再此以前須要理解反射和動態代理,推薦兩篇不錯的文章:反射動態代理

構建完 Retrofit 對象後,須要調用 retrofit.create(ApiService.class), 生成 ApiService 接口對應的動態代理對象。

public <T> T create(final Class<T> service) {
  // ......
  return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
      new InvocationHandler() {
        // 獲取目前的平臺,對於咱們來講就是Android平臺
        private final Platform platform = Platform.get();
        @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
            throws Throwable {
          // ......
          
          // 構建 接口方法 對象,此對象主要是解析註解並拼接成請求所需的參數。
          // 它是最後用來給okhttp使用並真正發送請求。
          ServiceMethod<Object, Object> serviceMethod =
              (ServiceMethod<Object, Object>) loadServiceMethod(method);
          // 本質上就是Retrofit對Okhttp的Call對象進一步的封裝
          OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
          // 最後默認返回的也是一個Call可執行任務對象(固然RxJava返回的對象就不是默認的了),
          // 其實相似因而OkHttpCall的代理類,只不過內部加了切換到主線程的操做。
          return serviceMethod.callAdapter.adapt(okHttpCall);
        }
      });
}
複製代碼

部分沒啥用的代碼省略了,剩下的都是關鍵代碼,而且加了註釋。這裏再說下動態代理相關的,create 方法返回值是第一部分的使用步驟中的 apiService, 它是動態代理生成的對象。調用動態代理對象的方法後會調到 invoke 方法,返回值對應使用步驟中的 call 對象。

進入 loadServiceMethod 方法,能夠看到對 serviceMethod 會有一個緩存,一個方法只會解析一次,以後重複利用。而後經過 build 構建一個 ServiceMethod 對象。

public ServiceMethod build() {
  // 跟進 createCallAdapter 能夠看到它返回的 CallAdapter 是根據方法的返回類型,
  // 如本文使用步驟中返回類型是 Call,那麼就會返回以前已建立過的默認 CallAdapter
  callAdapter = createCallAdapter();
  // 返回結果類型,對於 Retrofit 默認返回結果類型是 ResponseBody,
  // 所以在閱讀源碼時直接把 responseType 看作 ResponseBody 類型。
  responseType = callAdapter.responseType();
  
  // ......
  
  // 跟進 createResponseConverter 方法能夠看到它是根據 responseType 返回對應的 Converter。
  responseConverter = createResponseConverter();
  // 遍歷解析方法上的註解如 @GET、@POST等(拆分註解中的字符串,將參數名記錄)
  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation);
  }
  
  // ......

  for (int p = 0; p < parameterCount; p++) {
    Type parameterType = parameterTypes[p];
    // ......
    Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
    // ......
    
    // 遍歷解析形參的註解,如@Path、@Query等.
    parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
  }
  
  // ......
  
  return new ServiceMethod<>(this);
}
複製代碼

其實 ServiceMethod 不是理解 Retrofit 的關鍵,只需知道它封裝瞭解析註解邏輯和記錄參數。

直接看下 OkhttpCall 的enqueue方法

@Override public void enqueue(final Callback<T> callback) {
  okhttp3.Call call;
  synchronized (this) {
        // ......
        
        // 這裏的 call 就是 OkHttp 的 call
        call = rawCall = createRawCall();
    
  }
  // ......
  
  // okhttp 的 call 異步執行並回調,注意這裏依然在異步線程中。
  call.enqueue(new okhttp3.Callback() {
    @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawRespon
        throws IOException {
      Response<T> response;
      try {
        response = parseResponse(rawResponse);
      } catch (Throwable e) {
        callFailure(e);
        return;
      }
      callSuccess(response);
    }
    
    // ....
    
    private void callSuccess(Response<T> response) {
      try {
        callback.onResponse(OkHttpCall.this, response);
      } catch (Throwable t) {
        t.printStackTrace();
      }
    }
  });
}
複製代碼

最後一步經過 callAdapter.adapt(okHttpCall) 返回一個最終可執行對象,這裏咱們都是看默認的,所以 callAdapter 是 ExecutorCallAdapterFactrory 中 get 獲取的 callAdapter, 其 adapter 方法返回的是 ExecutorCallbackCall 也是實現了 Call 接口,以前說過此對象是 okHttpCall 的代理對象,所以傳入 okHttpCall 實例,而內部乾的最關鍵的一件事就是在異步執行請求完成後經過 callBackExecutor(以前早已準備好的Handler切換) 切換到主線程。

3. 調用流程

調用流程

綠色線框表明咱們使用 Retrofit 須要作的幾步。

Retrofit 內部有大量的設計模式,設計的很是巧妙,多看源碼也能提升咱們的代碼設計能力。build 階段用了構造者模式,create 用了動態代理模式,CallAdapterFactory 和 ConverterFactory 用了適配器模式,等等。

3、 自定義ConverterFactory

爲了加深對Retrofit的理解以及體會它的好用程度,寫了一個自定義ConverterFactory。

public final class StringConverterFactory extends Converter.Factory {

    private final static String TAG = "StringConverterFactory";

    @Nullable
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        if(type == String.class){
            return StringResponseConverter.INSTANCE;
        }
        return null;
    }

    @Nullable
    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        if(type == RequestBody.class){
            return StringRequestBodyConverter.INSTANCE;
        }
        return null;
    }

    final static class StringResponseConverter implements Converter<ResponseBody, String> {
        final static StringResponseConverter INSTANCE = new StringResponseConverter();

        @Override
        public String convert(ResponseBody value) throws IOException {
            Log.d(TAG, value.toString());
            return value.string();
        }
    }

    final static class StringRequestBodyConverter implements Converter<RequestBody, RequestBody> {
        final static StringRequestBodyConverter INSTANCE = new StringRequestBodyConverter();

        @Override
        public RequestBody convert(RequestBody value) throws IOException {
            Log.d(TAG, "no change, hahaha...");
            return value;
        }
    }

}

複製代碼

支持String的泛型參數,其實內部也就是把 ResponseBody 提早轉成 String 並打印而已。在使用的時候須要在構建 Retrofit 時添加:

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://gank.io") 
    .addConverterFactory(new StringConverterFactory())
    .build();
複製代碼

4、總結

最後對 Retrofit 作一個總結 。Retrofit 是對 okhttp3 的封裝,其最大的特性就是解耦,你能夠自定義不少你想要的東西,最知名的就是 RxJava 的配合使用。

首先調用 build 方法建立生產 okhttp3 的 call 對象的callFactory、建立用來切換線程的 callBackExecutor、建立生產 CallAdapter 的 CallAdapterFactory、建立生產 Converter 的 ConverterFactory。

接着調用 retrofit.create 建立動態代理對象,調用接口方法會觸發 invoke 方法,invoke 內建立 ServiceMethod並作了註解解析、建立OkhttpCall(對okhttp3的call進一步封裝),最後經過 CallAdapter.adapt 方法返回可執行對象默認是 ExecutorCallBackCall。

最後調用 call.enqueue 後 okhttp3將請求 ServiceMethod 解析好的url和參數,最終返回結果會被 Converter.convert 解析成你想要的數據模型(默認是ResponseBody),最後經過 callBackExecutor.execute 切換到主線程將數據回調給開發者。

ps:作完 Retrofit 源碼分析後,還想看下 RxJava 的源碼,爲了更深刻理解 RxJava 和 Retrofit 共同使用的原理。最近想到一句話感受蠻有意思的:當你不知道作什麼的時候,去看看源碼或官方文檔吧。

相關文章
相關標籤/搜索