Android 網絡框架之Retrofit源碼解析

在使用了Retrofit以後,你是否會有這樣的疑問:html

一、爲何有OkHttp這麼強大的網絡框架了,Retrofit還能脫穎而出?
二、Retrofit是怎麼適配第三方框架的?
三、Retrofit用了哪些設計模式?
四、Retrofit爲何好用?java

下面咱們就來探索Retrofit的真面目吧!react

本文基於如下Java版本解析:android

// retrofit
implementation 'com.squareup.retrofit2:retrofit:2.5.0'  
implementation 'com.squareup.retrofit2:converter-gson:2.0.2'  
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'

// rxjava
implementation 'io.reactivex.rxjava2:rxjava:2.1.6'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
複製代碼

本文目錄:git

  • 一、Retrofit存在的意義
  • 二、Retrofit的使用
  • 三、Retrofit的建立
  • 四、Retrofit.Builder的建立
  • 五、Retrofit的核心,動態代理
  • 六、Retrofit的動態代理
  • 七、總結

一、Retrofit存在的意義

Retrofit不是一個純網絡框架,爲何這麼說呢? Retrofit是基於OkHttp框架來實現請求的,而Retrofit是基於OkHttp框架實現的一套封裝,利用動態代理實現了簡單的網絡請求實現,並支持Gson,RxJava等第三方框架的適配,簡而言之,就是讓你的網絡請求更便捷,更強大了;github

那麼Retrofit底層究竟是怎麼封裝的呢? 是怎麼變得更便捷,更強大了呢?編程

接下來讓咱們來深刻源碼,一探究竟!json

二、Retrofit的使用

光說源碼,不講使用,總感受有點被架空,容易找不到點;設計模式

那麼接下來咱們先來看一下Retrofit的簡單使用吧;api

Retrofit接口:

public interface GetRequestInterface {

    @GET("openapi.do?keyfrom=Yanzhikai&key=2032414398&type=data&doctype=json&version=1.1&q=car")
    Call<ResultData> getCall();

    @GET("openapi.do?keyfrom=Yanzhikai&key=2032414398&type=data&doctype=json&version=1.1&q=car")
    Observable<ResultData> getObservableCall();

    @GET("openapi.do?keyfrom=Yanzhikai&key=2032414398&type=data&doctype=json&version=1.1&q=car")
    Call<StudentBean> getItem(@Query("student") String student, @Query("type") String type);
}
複製代碼

基礎請求URL:

public class BaseRequest {

    public static final String BaseURL = "https://fanyi.youdao.com/";

}
複製代碼

Retrofit的使用:

// Retrofit 實例的建立
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BaseRequest.BaseURL)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();

        // 建立請求接口類
        GetRequestInterface request = retrofit.create(GetRequestInterface.class);
    	// ---普通網絡請求--- 
        // 獲取請求對象Call
        Call<ResponseBody> call = request.getCall();
        // 執行網絡請求
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                ResponseBody body = response.body();
                Log.i("TAG", "MainActivity onResponse response:" + response.toString());
                Log.i("TAG", "MainActivity onResponse body:" + (body == null ? "null" : body.toString()));
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                Log.i("TAG", "MainActivity onFailure t:" + t.toString());
            }
        });
        
        // ---RxJava網絡請求--- 
        Observable<ResultData> observableCall = request.getObservableCall();

        observableCall.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<ResultData>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(ResultData resultData) {
                        Log.i("TAG", "retrofit onNext resultData:" + resultData.toString());

                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onComplete() {
                        Log.i("TAG", "retrofit onComplete ");
                    }
                });
複製代碼

主要分爲幾步:

(1)經過構造者模式建立Retrofit實例;
(2)經過動態代理建立接口的實例;
(3)經過接口的實例獲取到網絡請求的操做類Call;
(4)經過Call來執行網絡請求;

看一下大體流程圖:

大體流程圖

下面咱們基於使用來深刻框架,探索其源碼的精妙!

三、Retrofit的建立

3.一、Retrofit的變量

// Retrofit 實例的建立
        Retrofit retrofit = new Retrofit.Builder()
        		// 建立baseUrl
                .baseUrl(BaseRequest.BaseURL)
                // 添加GsonConverterFactory
                .addConverterFactory(GsonConverterFactory.create())
                // 添加RxJava的RxJavaCallAdapterFactory
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                // 構建Retrofit的實例
                .build();
複製代碼

Retrofit的構建使用了建造者模式,這個模式的優勢就是能夠構造複雜的對象,方便擴展,而且看起來代碼比較簡潔,美觀;

在開始以前,咱們先來看一下Retrofit的成員變量;

這裏的變量並非不少,咱們來一個個分析;

**(1)Map<Method, ServiceMethod<?>> serviceMethodCache:**這是一個方法的緩存類,key爲網絡請求的Method,好比GET,POST等,而ServiceMethod則對應着動態代理解析後的方法類;
**(2)okhttp3.Call.Factory callFactory:**這個是建立OkHttp的工廠類;
**(3)HttpUrl baseUrl:**這個是基礎URL,網絡請求會帶上這個基礎URL;
**(4)List<Converter.Factory> converterFactories:**Converter.Factory的集合,Converter.Factory是將返回的數據經過這個工廠轉化爲對應的數據,好比Gson的GsonConverterFactory工廠類,也就是數據轉化器工廠;
**(5)List<CallAdapter.Factory> callAdapterFactories:**CallAdapter.Factory的集合,CallAdapter.Factory是網絡請求的適配器工廠,好比把Call轉化爲RxJava請求的RxJavaCallAdapterFactory工廠,也就是Call轉化工廠;
**(6)Executor callbackExecutor:**用於回調網絡請求;
**(7)boolean validateEagerly:**用於判斷是否須要當即解析方法,這個咱們在將動態代理的時候會講到;

咱們看完了Retrofit的成員變量,可是Retrofit的建立是經過Builder來建立的,下面咱們來看看Retrofit的Builder的變量有哪些,分別有哪些做用;

四、Retrofit.Builder的建立

4.一、Retrofit.Builder的變量

從圖片能夠看出,和Retrofit的變量差很少,惟一有區分的就是多了Platform和validateEagerly變量,讓咱們經過源碼來看看這兩個是作什麼的;

先來看一下這個Platform的賦值;

這個Platform是經過Platform.get()方法獲取的,來看看這個方法是啥邏輯;

從圖片能夠看出,最終是調用的findPlatform()方法;

這個方法會返回Android,Java,Platform等類,主要是用來判斷對於的平臺,而後獲取到對於的Platform;

private static Platform findPlatform() {
    try {
      Class.forName("android.os.Build");
      // 判斷從系統中獲取到的SDK不爲0的時候,則爲Android平臺;
      if (Build.VERSION.SDK_INT != 0) {
        return new Android();
      }
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("java.util.Optional");
      // 若是不是Android平臺的話,那麼就返回Java的平臺;
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    // 默認的返回值;
    return new Platform();
  }
複製代碼

而Build.VERSION.SDK_INT的判斷邏輯爲:

翻譯過來的意思就是:

當前在此硬件上運行的軟件的SDK版本。這個值在設備啓動時不會改變,但可能會在硬件製造商提供OTA更新時改變;

下面咱們來看一下這個Android類對應的源碼:

Android類對應的源碼很少,就幾個方法,這裏咱們重點關注下面這幾個方法:
**(1)defaultCallbackExecutor():**默認的線程執行器Executor,從源碼能夠看出,獲取的是主線程的Hander,execute的時候,會post到主線程執行;
**(2)defaultCallAdapterFactories:**獲取默認的CallAdapter.Factory,用於建立CallAdapter;
**(3)defaultConverterFactories:**獲取默認的數據轉換工廠ConverterFactory,用於建立轉換器Converter;

總結:Platform主要是用於適配不一樣的平臺,用於獲取默認的Executor,請求適配器工廠類CallAdapterFactory,數據轉換工廠類ConverterFactory等;

4.二、baseUrl

baseUrl,是咱們網絡請求的基礎URL;

咱們進源碼裏面看一下,具體作了啥操做;

這裏主要作了兩步操做:
一、首先第一步是經過解析這個baseUrl並返回一個HttpUrl對象; 二、第二步是將第一步建立的HttpUrl對象賦值給Builder;

這裏咱們主要看第一步的具體操做,這裏的邏輯是在HttpUrl的 parse(@Nullable HttpUrl base, String input)裏,經過解析URL,判斷該請求的scheme是爲http仍是https,若是不是這其中一個,那麼就會拋出異常,源碼咱們大體看一下;

而後下面還會解析URL,獲取到主機地址host,端口號port,具體源碼我就不貼了,感興趣的能夠跟着源碼看一下;

4.三、Converter.Factory

這個咱們上面在接受Retrofit的成員變量的時候有提過,是用於建立Converter的工廠,使用了抽象工廠的設計模式,而Converter是用來將請求返回的數據,轉化爲對應平臺的數據,而這裏,咱們使用的是Gson平臺;

咱們先來看一下這個Converter.Factory,看看其背後是怎麼實現的;

從源碼能夠看出,Converter.Factory是Converter的內部類,主要有兩個方法,一個是requestBodyConverter,用於將請求的RequestBody轉換爲對應的轉換器Converter;

另外一個方法是responseBodyConverter,用於將返回的返回體ResponseBody轉換爲對應的轉換器Converter;

而轉換器Converter裏面只有一個方法convert,用於將返回的數據轉換爲對應的類型;

咱們在建立Retrofit的實例時,是經過GsonConverterFactory.create()來建立對應的轉換器工廠的,下面咱們來看看這個Gson的轉換器工廠是怎麼實現的;

先來看一下這個create()的方法;

最終是走的這裏,進行了簡單的賦值;

GsonConverterFactory這個工廠最重要的仍是responseBodyConverter和requestBodyConverter方法,下面咱們來具體分析;

  • requestBodyConverter:

在requestBodyConverter方法裏面,經過class的Type類型,建立了Gson的TypeAdapter,這個TypeAdapter很熟悉,就是咱們使用gson解析會用到的類,最終經過TypeAdapter和gson的參數建立了GsonRequestBodyConverter對象,下面來瞄一眼這個類的代碼;

這個類的源碼很簡單,咱們主要convert()這個方法;

這個方法的邏輯就是將傳進來的value值經過TypeAdapter將其轉化爲ByteString,而後再傳進RequestBody做爲參數來構建RequestBody對象;

這個方法咱們先了解到這裏,後面在這個requestBodyConverter調用的地方再來說一下;

  • responseBodyConverter:

這個方法和上面那個requestBodyConverter方法有點相似,也是經過class的Type類型,建立了Gson的TypeAdapter,最終經過TypeAdapter和gson的參數建立了GsonResponseBodyConverter,同理,咱們也來看一下這個類的實現吧;

源碼很簡單,這裏咱們也是關注convert()這個方法;

這裏的邏輯有沒有很熟悉,就是咱們常常用的gson解析,經過TypeAdapter讀取JsonReader的數據,返回對應的數據類型,這裏的參數ResponseBody就是咱們上面GsonRequestBodyConverter的convert方法生成的;

到這裏GsonConverterFactory就講的差很少了,後面咱們在用到的地方再詳細講一下;

4.五、CallAdapter.Factory

CallAdapter.Factory,從命名能夠看出,是用來建立CallAdapter的工廠類,使用了抽象工廠的設計模式,而CallAdapter是用於將Call轉化爲咱們所須要的請求類型,好比將Call轉化爲RxJava的調用類型;

而CallAdapter裏面是經過adapt方法來進行轉換的,adapt是接口的一個方法,交給子類去實現,這個方法的邏輯,咱們下面將Retrofit的解析時,再統一講解,這裏是須要瞭解這是一個轉換的方法便可;

下面咱們來看看建立CallAdapter的工廠裏面都有哪些方法,分別是用來幹嗎的;

這個Factory的邏輯不多,只有幾個方法,這裏咱們主要關注get方法,經過get方法來獲取CallAdapter的對象,同理,這裏也是交給子類去實現;

而上面咱們Retrifit的建立,在CallAdapter.Factory的添加時,使用了RxJava的工廠,也就是RxJava2CallAdapterFactory,用於將Call請求轉換爲RxJava對應的請求;

下面咱們來看看這個RxJava2CallAdapterFactory的實現邏輯吧;

RxJava2CallAdapterFactory的建立,也是經過RxJava2CallAdapterFactory.create()的方法,那麼咱們來看下這個create方法作了啥?

只是簡單的new了一個RxJava2CallAdapterFactory,而構造方法裏也沒有其餘的邏輯了,只是對Scheduler進行賦值,而這裏建立時,傳的是null;

上面咱們看完RxJava2CallAdapterFactory的建立後,下面咱們來看一下RxJava2CallAdapterFactory是怎麼經過get方法建立一個CallAdapter的;

public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    Class<?> rawType = getRawType(returnType);

    if (rawType == Completable.class) {
      // 建立RxJava2CallAdapter
      return new RxJava2CallAdapter(Void.class, scheduler, isAsync, false, true, false, false,
          false, true);
    }
	...
	// 建立RxJava2CallAdapter
    return new RxJava2CallAdapter(responseType, scheduler, isAsync, isResult, isBody, isFlowable,
        isSingle, isMaybe, false);
  }
複製代碼

這個方法職責很明確,就是根據各類參數來建立RxJava的CallAdpter,也就是RxJava2CallAdapter;

這裏咱們來重點關注一個RxJava2CallAdapte的adapt方法的邏輯;

public Object adapt(Call<R> call) {
	// 第一步:根據是不是異步的參數建立對應的Observable
    Observable<Response<R>> responseObservable = isAsync
        ? new CallEnqueueObservable<>(call)
        : new CallExecuteObservable<>(call);

    Observable<?> observable;
    // 第二步:根據各類判斷,再封裝一層Observable返回
    if (isResult) {
      observable = new ResultObservable<>(responseObservable);
    } else if (isBody) {
      observable = new BodyObservable<>(responseObservable);
    } else {
      observable = responseObservable;
    }
    ... 
    return observable;
  }
複製代碼

這個方法的邏輯並複雜,主要是將Call請求轉化爲RxJava的請求,最終返回一個RxJava的被觀察者:Observable,用於進行RxJava類型的網絡請求,如上面的示例;

這個方法的邏輯主要有兩步,咱們先來看一下第一步建立的被觀察者,這裏會先判斷是不是異步,若是是異步的話,那麼就建立CallEnqueueObservable,不然就建立CallExecuteObservable;

這兩個的區別就是,在調用訂閱(subscribe)的時候,會執行CallEnqueueObservable的subscribeActual方法,最終是經過OkHttpCall的enqueue方法來執行異步請求;

而CallExecuteObservable在調用訂閱(subscribe)的時候,也會執行CallEnqueueObservable的subscribeActual方法,在這個方法裏,就直接調用OkHttpCall的execute方法來執行同步請求;

而第二步的封裝,這裏咱們主要以BodyObservable來進行講解,這個類會對訂閱的觀察者進行封裝,在onNext方法中將body返回;這一步能夠理解爲對返回的結果進行處理;

這裏是RxJava的用法,我默認你是會的,若是對RxJava還不熟悉的同窗,能夠後面去看看RxJava的用法;

到此,Retrofit的CallAdapter.Factory的邏輯就先告一段落了,下面咱們來看看Retrofit的最終build()方法的邏輯;

4.五、Retrofit.Builder#build

廢話很少說,咱們直接擼源碼;

public Retrofit build() {
      // 判斷當callFactory(OkHttpClient)爲空,就從新建立一個OkHttpClient進行賦值;
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

	  // 判斷Executor爲空時,就用Platform的默認Executor進行賦值,上面咱們講過,這裏面使用的是主線的的Handler;
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // 經過添加的CallAdapter.Factory來建立一個新的CallAdapter.Factory集合;
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
     
// 添加Platform的默認CallAdapter.Factory,若是咱們沒有添加CallAdapter.Factory,那麼就會使用這個Platform的默認CallAdapter.Factory; callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

      // 建立Converter.Factory的集合
      List<Converter.Factory> converterFactories = new ArrayList<>(
          1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

      // Add the built-in converter factory first. This prevents overriding its behavior but also
      // ensures correct behavior when using converters that consume all types.
      // 添加默認的Converter.Factory
      converterFactories.add(new BuiltInConverters());
      // 添加自定的Converter.Factory
      converterFactories.addAll(this.converterFactories);
      // 添加Platform的默認Converter.Factory
      converterFactories.addAll(platform.defaultConverterFactories());
	  // 最終建立Retrofit實例;
      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }
複製代碼

這個build()方法,主要是作各類參數的賦值,最終經過參數來建立Retrofit的實例,那麼到這裏Retrofit的建立就差很少將完了,下面咱們將會學習到Retrofit的核心;

爲何咱們能夠經過接口定義一個類型,就能夠執行請求了,對於這些方法的解析,以及參數的賦值的操做是在哪裏呢?

這麼就涉及到Retrofit使用的一個很重要的設計模式了,也就是動態代理設計模式;

五、Retrofit的核心,動態代理

5.一、什麼是代理?

舉個例子,假如我要去超市買水果,但是我好懶,週末就想呆在家裏不想出門,可是內心又很想吃水果,那怎麼辦呢?

只能打開外賣App,在上面買完以後,由外賣小哥送過來,這時候,我就經過中介,外賣App來買到水果,而這個過程叫作代理;

不直接操做,而是委託第三方來進行操做,從而達到目的;

而Java的代理分爲靜態代理和動態代理;

5.二、什麼是靜態代理?

若是代理類在程序運行以前就已經存在了,那麼這種代理方式就被稱爲靜態代理;

仍是以上面的例子,我要買水果,來定義一個買水果的接口Operator;

public interface Operator {
	// 買水果
	void buyFruit();
}
複製代碼

而咱們的代理類外賣App須要實現這個接口,同時,將須要委託的對象傳進來,在buyFruit的過程當中,作了一些出來,好比去超市取貨,取完貨以後,再送貨;

public class AppAgent implements Operator {
    
    private Operator operator;

    public AppAgent(Operator operator) {
        this.operator = operator;
    }

    @Override
    public void buyFruit() {
    	// 一、在App上,提供商品給用戶下單
        
        // 二、根據訂單去超市採購水果
        operator.buyFruit();
        
        // 三、送貨給客戶
    }
}
複製代碼

委託的對象,超市:

public class Market implements Operator {

    @Override
    public void buyFruit() {
		// 到超市買水果
    }
}
複製代碼

那麼最終的實現效果就是,咱們經過外賣App的一頓操做,從超市買到了水果,以下:

public class Main {

    public static void main(String[] args) {

        // 委託對象,超市;
        Market market = new Market();
        // 代理對象,外賣App;
        AppAgent appAgent = new AppAgent(market);
        // 經過外賣App的代理,從超市買到了水果;
        appAgent.buyFruit();
    }
}

複製代碼

以上就是咱們靜態代理的過程,這個例子只是舉了買水果這個過程,可是若是我還想要買菜,買生活用品等一系列東西呢?

我就得在接口Operator裏面再多新增好幾個方法,一樣的代理類也要跟着去重寫一堆的方法,可是這些方法作的操做其實都是同樣的,都是買這個動做,可是咱們不得已,新增一種類型,咱們就得在代理類裏面再重寫並調用;

那麼這個過程實際上是能夠抽出來的,這種方式就叫作動態代理;

5.三、動態代理

動態代理,和靜態代理不一樣的是,動態代理的方法是運行後才建立的,而靜態代理是運行前就存在的了;

說白了,和靜態代理不一樣的是,動態代理的方法都是在運行後,自動生成的,因此叫動態代理;

下面咱們來看看動態代理是咋用的;

在使用動態代理的時候,被代理類須要實現InvocationHandler這個接口,,這個invoke方法是在動態生成的代理類中調用的,對應着咱們上面在靜態代理operator.buyFruit()這個方法的調用,下面來看一下這個方法對應的參數;

public interface InvocationHandler { 
  // Object proxy:接口的具體實現類;
  // Method method:解析以後自動生成的方法;
  // Object[] args:方法對於的參數;
  Object invoke(Object proxy, Method method, Object[] args);
}
複製代碼

而最終運行時生成的代理類,通常名稱會是 P r o x y 0 Proxy0, Proxy1這種,經過Proxy.newProxyInstance()方法來生成的,這個下面會講到,先來看一下下面的僞代碼;

public final class $Proxy0 extends Proxy implements Operator { 
	public final boolean buyFruit() { 
    	// h是InvocationHandlel,調用了invoke方法;
    	super.h.invoke(this, m1, (Object[])null); 
    }
  }
}
複製代碼

在生成的代理類中,會實現咱們的接口,並重寫方法,在方法裏面經過InvocationHandler回調參數到invoke方法裏,最終經過反射調用被代理對象的方法;

而咱們經過實現這個InvocationHandler接口,在invoke方法裏面,經過method.invoke(object, args)能夠來調用被代理的方法,而後咱們能夠在這個method.invoke(object, args)以前或者以後作一些處理,這樣因此的方法均可以一併進行處理,而不是每次新增一個方法,就得重寫一遍邏輯;

下面來看一下具體實現:

public class CustomProxy implements InvocationHandler {

    private Object object;

    public CustomProxy(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 一、在App上,提供商品給用戶下單
        // doSomeThing();

        // 二、根據訂單去超市採購東西(在這個方法以前或者以後均可以統一處理操做)
        Object invoke = method.invoke(object, args);

        // 三、送貨給客戶
        // doSomeThing();
        return invoke;
    }
}
複製代碼

下面來看一下,最終的調用;

public class Main {

    public static void main(String[] args) {
        // 建立被代理類
        CustomProxy customProxy = new CustomProxy(new Market());
        // 動態生成代理類
        Operator proxyInstance = (Operator) Proxy.newProxyInstance(Operator.class.getClassLoader(), new Class[]{Operator.class}, customProxy);
        // 調用對應的方法
        proxyInstance.buyFruit();
    }
}
複製代碼

這裏經過Proxy.newProxyInstance()方法,動態生成代理類$Proxy0這種,這裏涉及到的反射的知識,就再也不贅述了;

而後動態代理類調用方法,最終會走到CustomProxy的invoke()方法,而後咱們在這個方法裏面經過method.invoke(object, args)來進行最終的代理調用,而在這個最終的代理調用的先後,咱們能夠實現自定義的邏輯;

這個實現了InvocationHandler接口的CustomProxy,更像是一個攔截類,在代理方法的調用過程當中進行攔截,而後再實現咱們的自定義邏輯;

至此,動態代理你理解了嗎?沒有理解也不要緊,多看幾遍,多練幾遍就能夠了;

六、Retrofit的動態代理

6.一、Retrofit爲何要使用動態代理?

首先,讓咱們來想一個問題,Retrofit爲何要使用動態代理?

使用動態代理的好處就是在調用方法以前,咱們能夠統一作一些操做,而沒必要新增一個方法就去寫一遍邏輯;

而Retrofit巧妙的使用了動態代理在調用接口的方法以前,統一的去解析處理Header和URL等操做;

這樣就不用每次在新增一個請求的方法,就去寫一遍這個解析的邏輯;

那麼接下來咱們來看看Retrofit的怎麼解析這些接口的;

6.二、Retrofit是怎麼使用動態代理的?

下面咱們來看一下Retrofit的create的方法,動態代理的邏輯是在這裏實現的;

public <T> T create(final Class<T> service) {
    ...
    // 驗證接口的參數以及配置是否正確
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    // 動態代理
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();
          private final Object[] emptyArgs = new Object[0];

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
            // 判斷是不是Object,若是是的話,就直接調用方法返回
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            // 判斷是不是Java8平臺的默認方法類型,若是是的話,就調用Java8平臺的invokeDefaultMethod方法
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            // 解析方法;
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }
複製代碼

這裏咱們將這個方法分爲兩步;

** 第一步:** eagerlyValidateMethods方法,這個方法的邏輯是用於加載接口的配置,用於判斷接口對應的header,body以及方法的參數等配置是否正確,若是不正確那麼就會拋出異常;

** 第二步:** loadServiceMethod方法,這個方法的邏輯主要是用於解析咱們在接口配置的註解以及參數,好比header,body,url等等;

這裏咱們重點關注第二步的loadServiceMethod方法方法;

咱們來看一下其源碼的具體實現;

ServiceMethod<?> loadServiceMethod(Method method) {
    // 先從緩存map集合裏面獲取ServiceMethod;
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        // 若是從緩存map裏面獲取不到ServiceMethod,那麼再經過解析註解,獲取到ServiceMethod對象;
        result = ServiceMethod.parseAnnotations(this, method);
        // 將解析後的ServiceMethod對象存入到map集合中;
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }
複製代碼

這裏作的操做很簡單,就是獲取ServiceMethod,而在獲取ServiceMethod的過程當中,會先從緩存的map中獲取,若是獲取不到了再進行解析,這樣就沒必要獲取一次ServiceMethod,就去解析一次,比較耗性能;

而這個ServiceMethod的類是個抽象類,只有兩個方法,一個是靜態的parseAnnotations方法,一個是抽象的invoke方法;

咱們先來看一下這個parseAnnotations方法;

static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
	// 經過解析接口方法的註解,獲取RequestFactory
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

   	...
    // 解析註解並獲取ServiceMethod對象
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }
複製代碼

這個方法的邏輯也不是很複雜,主要分爲兩步;

第一步: 獲取RequestFactory;
第二步: 獲取ServiceMethod;

咱們先來看第一步的操做;

static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
    return new Builder(retrofit, method).build();
  }
複製代碼

經過Builder來建立RequestFactory,來看看這個Builder作了啥操做;

Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      // 獲取方法全部的註解,包括本身聲明的以及繼承的
      this.methodAnnotations = method.getAnnotations();
      // 獲取方法參數的全部類型,包含泛型;
      this.parameterTypes = method.getGenericParameterTypes();
      // 獲取方法參數上的全部註解
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }
複製代碼

這個方法的邏輯很簡單,就是作一些賦值操做,這裏須要注意的是這幾個反射的方法,下面的build方法會用到;

RequestFactory build() {
      for (Annotation annotation : methodAnnotations) {
        // 遍歷方法的註解,解析方法的參數配置,獲取到請求的url,header等參數
        parseMethodAnnotation(annotation);
      }

      ...
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
        // 遍歷方法的參數,以及參數的類型,解析方法的參數邏輯
        parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p]);
      }

      ...
      // 根據上面解析的參數配置,建立RequestFactory
      return new RequestFactory(this);
    }
複製代碼

這個方法的邏輯就比較重要了,咱們在接口的方法裏面定義的相關url,header等註解,最終就是在這裏解析並轉化爲okhttp請求的Call,那麼咱們來看看這裏究竟是怎麼解析的;

先來看一下parseMethodAnnotation這個方法, 這個方法的主要邏輯是用於解析方法註解的配置信息;

下面咱們來看看這個類的具體實現;

這個方法的邏輯比較多,咱們大體看一下就能夠了,這裏面作的主要職責就是經過註解Annotation,獲取到url,header,isMultipart等參數,並將其賦值給建造者Builder的成員變量;

對於這個方法,這裏就很少說了,感興趣的跟着源碼去看一下;

而第二個方法parseParameter,也是遍歷上面獲取到的方法的參數類型parameterTypes以及方法參數的註解parameterAnnotationsArray,來解析並獲取相關配置,而這個方法最終是調的parseParameterAnnotation方法的邏輯;

主要是用於解析這裏的邏輯:

下面咱們來看看具體實現;

這個parseParameterAnnotation方法的邏輯和上面的parseMethodAnnotation方法有點相似,也是經過判斷對於的類型來進行解析,這裏源碼過長,就不貼出來了;

這裏咱們就找其中一個Query的解析來進行分析;

省略前面的代碼
... 
 else if (annotation instanceof Query) {
        ...
        // 將註解annotation強轉爲Query
        Query query = (Query) annotation;
        // 獲取註解Query對應的值;
        String name = query.value();
        ...
        if (Iterable.class.isAssignableFrom(rawParameterType)) {
          // 判斷這個參數的類型爲Class類型;
          ...
          // 建立ParameterHandler的子類Query
          return new ParameterHandler.Query<>(name, converter, encoded).iterable();
        }
        ...
      }
...
省略後面的代碼
複製代碼

這裏最終解析獲取的是ParameterHandler.Query類,這個類裏面有個apply的方法,會在接口請求的時候會調用到,目的是經過請求的RequestBuilder將解析出來的參數,添加到RequestBuilder裏去;

這裏咱們瞭解一下就能夠了;

這裏還有一個須要注意的點,就是這裏的解析,涉及到Converter這個convert方法的邏輯;

這裏我以Body爲例,在Body這個類的apply方法裏,會經過Converter這個convert方法,將body參數轉化爲對應類型的數據;

這個apply的方法是在進行網絡請求的時候會調用,具體調用鏈以下,這裏以RxJava爲例:

一、RxJavaCallAdapterFactory.ResponseCallAdapter的adapt方法;
二、RxJavaCallAdapterFactory.CallOnSubscribe的call方法;
三、RxJavaCallAdapterFactory.RequestArbiter的request方法;
四、OkHttpCall的execute方法;
五、OkHttpCall的createRawCall方法;
六、RequestFactory的create方法;
七、ParameterHandler的apply方法;

最終在這裏觸發了apply方法的調用,這裏源碼就不貼出來了,感興趣的朋友,能夠跟着源碼去看一遍;

咱們上面看的RequestFactory的build方法的最後一個,建立RequestFactory,這裏的邏輯就是將咱們上面解析出來的參數,給這個RequestFactory進行賦值;

那麼到這裏parseAnnotations的第一步就講完了,先來總結一下,這一步主要是解析方法註解和方法參數的註解,從而獲取到對應的參數配置,最終將其賦值給RequestFactory對象;

下面咱們來看HttpServiceMethod的parseAnnotations方法;

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) {
     // 建立CallAdapter
    CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method);
    ...
	// 建立Converter
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);

	// 經過參數建立HttpServiceMethod
    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    return new HttpServiceMethod<>(requestFactory, callFactory, callAdapter, responseConverter);
  }
複製代碼

這個方法的邏輯並不複雜,主要分三步:

第一步: 建立CallAdapter;
第二步: 建立Converter;
第三步: 經過參數建立HttpServiceMethod;

這裏主要關注的是前兩步,建立了CallAdapter和Converter;

咱們先來看一下第一步的邏輯;

這裏的調用鏈是這樣的,首先調用了HttpServiceMethod的createCallAdapter方法,而後再調用了Retrofit的callAdapter方法,最後調用了Retrofit的nextCallAdapter方法;

咱們來看看nextCallAdapter的邏輯;

public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    ...
	// 經過callAdapterFactories的get方法來建立CallAdapter
    int start = callAdapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
      CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }

   ...
  }
複製代碼

這裏的邏輯不復雜,最終經過CallAdapter.Factory來建立CallAdapter,而這個CallAdapter.Factory的邏輯咱們上面已經講過了,就是用來建立CallAdapter的工廠;

下面咱們來看第二步的調用;

第二步的調用鏈是這樣的,先調用HttpServiceMethod的createResponseConverter方法,而後再調用Retrofit的responseBodyConverter方法,最終調用了Retrofit的nextResponseBodyConverter方法,咱們來看看nextResponseBodyConverter這個方法的邏輯;

public <T> Converter<ResponseBody, T> nextResponseBodyConverter( @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
    ...

    int start = converterFactories.indexOf(skipPast) + 1;
   // 先經過Converter.Factory工廠建立Converter,再經過Converter來轉化返回的數據
    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;
      }
    }

    ...
  }
複製代碼

第二步的邏輯和第一步相似,最終經過Converter.Factory工廠的responseBodyConverter方法建立了Converter,而這個Converter.Factory的邏輯,我在上面也已經講過了;

而這個Converter獲取的是返回體的Converter,用於將返回的數據轉化爲對應的平臺的數據,好比Gson,而這裏的Converter的convert的方法,最終是在OkHttp的網絡請求成功以後調用的,具體調用鏈以下:

一、OkHttpCall的enqueue方法;
二、OkHttpCall的parseResponse方法; 三、Converter的convert方法;

調用鏈也不是很複雜,感興趣的朋友能夠跟着源碼去看一下,這裏就不貼源碼了;

那麼到這裏,HttpServiceMethod的parseAnnotations方法就已經講完了;

下面,咱們回到Retrofit的create這個方法,在調用完loadServiceMethod方法以後,會調用ServiceMethod的invoke方法,這個方法是個抽象方法,具體實如今子類;

最終實現是在HttpServiceMethod的invoke方法裏;

在這裏調用了CallAdapter的adapt方法,將請求call轉化爲對應平臺的請求類型,好比RxJava的類型,這個上面也已經講過了,忘了的朋友,能夠往前翻翻;

七、總結

從上面的分析能夠看出,Retrofit最重要的點,就是動態代理,在動態代理的時候作了不少邏輯處理,簡化咱們後面的調用等等;

下面咱們來看最終的總結流程圖:

其餘

Android 你不得不學的HTTP相關知識

Android 網絡編程之TCP、UDP詳解

Android 網絡編程之HTTPS詳解

Android 網絡框架之OkHttp源碼解析

關於我

兄dei,若是個人文章對你有幫助的話,請幫我點個贊吧️,也能夠關注一下個人Github博客;

相關文章
相關標籤/搜索