Retrofit 源碼深入分析 —— Call 對(duì)象的誕生與請(qǐng)求

一、概述

Retrofit 是一個(gè)基于 OkHttp ,并遵循 Restful 風(fēng)格的一個(gè)網(wǎng)絡(luò)請(qǐng)求封裝庫,它將一個(gè)請(qǐng)求以接口的形式具現(xiàn)化的表現(xiàn)出來,將 OkHttp 的請(qǐng)求操作簡(jiǎn)化,并可以配置 convert 將響應(yīng)轉(zhuǎn)換為你想要的數(shù)據(jù) model,且支持異步或同步的操作,2.6.x 版本以上更是對(duì)協(xié)程進(jìn)行了支持,其優(yōu)異的設(shè)計(jì),簡(jiǎn)便的請(qǐng)求方式,推出沒多久就在 Android 開發(fā)領(lǐng)域出圈,目前 github star 數(shù)量已經(jīng)達(dá)到 37.8k 并被大量的 App 采用。
現(xiàn)如今更是成為各大公司面試的長(zhǎng)問問題?;诖耍酒恼聲?huì)從源碼層面深入分析 Retrofit 的運(yùn)行機(jī)制,了解 Retrofit 的內(nèi)部原理。

站在巨人的肩膀上眺望,就算最終我們成不了巨人,起碼也能比旁人看的更遠(yuǎn)不是。

二、Retrofit 基本流程圖

Retrofit 流程圖

如圖所示,接下來我們就按照流程圖來逐步分析 Retrofit 的源碼

三、一個(gè)簡(jiǎn)單的請(qǐng)求

閱讀源碼的最好方式就是從使用的角度從調(diào)用鏈一步步深入分析,這樣能讓我們避免在繁復(fù)紛雜的代碼中迷失方向,無所適從。所以讓我們從一個(gè)簡(jiǎn)單的請(qǐng)求開始,慢慢的揭開 Retrofit 神秘的面紗。

public interface GitHub {

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

public static final String API_URL = "https://api.github.com";

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");

// Fetch and print a list of the contributors to the library.
List<Contributor> contributors = call.execute().body();

for (Contributor contributor : contributors) {
  System.out.println(contributor.login + " (" + contributor.contributions + ")");
}

上述代碼是 Retrofit 官方 sample 中的例子,代碼并不是很多,可以看到經(jīng)過一些耳熟能詳?shù)臉?gòu)建步驟和一個(gè)聲明的 Restful 接口,就獲得了一個(gè)可以進(jìn)行 enqueue/sync 請(qǐng)求的 Call 對(duì)象,而通過這個(gè) Call 對(duì)象我們就可以直接獲取我們想要的 Response, 不得不佩服,真的是將網(wǎng)絡(luò)請(qǐng)求變得非常簡(jiǎn)便。

這里我特別說明一下 build 的構(gòu)建過程,有助于后面文章的理解

public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      //第一步創(chuàng)建一個(gè) OkHttpClient 對(duì)象
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

      //第二步創(chuàng)建一個(gè)回調(diào)線程 ,Platform 是在 Builder 構(gòu)造方法中進(jìn)行的實(shí)例化并傳入
      //主要作用判斷當(dāng)前的運(yùn)行環(huán)境,如果是 Android 則返回 AndroidCallback
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // 第三步將所有的 CallAdapter add 到 callAdapterFactories 集合中
      //例如常用的 RxJavaCallAdapterFactory
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

      // 第四步將所有的 Converter add 到 converterFactories 集合中
      //列如我們常用的 GsonConvertFactory
      List<Converter.Factory> converterFactories = new ArrayList<>(
          1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

      //添加 Retrofit 默認(rèn)提供的 converter ,當(dāng)我們的返回類型為 Call<ResponseBody> 就會(huì)使用這個(gè) BuiltInConverters
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      converterFactories.addAll(platform.defaultConverterFactories());

      //創(chuàng)建 Retrofit 實(shí)例
      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }

說實(shí)話,前幾年第一次看到這種網(wǎng)絡(luò)請(qǐng)求的方式的時(shí)候,感覺還是蠻秀的,竟然還能這么玩? 在使用了一段時(shí)間后,非常好奇它是怎做到的,相信有不少童鞋應(yīng)該也很好奇吧。接下來我們慢慢看看這么神奇的操作是怎么完成的~

四、Retrofit 的 create 方法

在進(jìn)行分析之前,如果你對(duì) Java 動(dòng)態(tài)代理、反射、注解沒什么了解的話,建議先簡(jiǎn)單的了解一下,否則可能無法愉快的閱讀后續(xù)的文章。

public <T> T create(final Class<T> service) {
    //校驗(yàn)接口是否可用
    validateServiceInterface(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 @Nullable Object invoke(Object proxy, Method method,
              @Nullable Object[] args) throws Throwable {
            // 調(diào)用的是成員類方法
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            //調(diào)用的是 Object 的默認(rèn)方法
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            //將解析的請(qǐng)求組裝成一個(gè) Call 對(duì)象返回
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }

create 方法接收一個(gè) Class<T> 類型的形參,內(nèi)部通過動(dòng)態(tài)代理的方式,在運(yùn)行時(shí)創(chuàng)建了 Github Api 的接口實(shí)現(xiàn)類,在調(diào)用接口方法時(shí),代理類會(huì)執(zhí)行 InvocationHandler 的 invoke 方法,而在 invoke 方法中我們就可以對(duì) Github 接口中聲明的方法進(jìn)行一些我們想做的改動(dòng)了。

可能說到這里,還是有點(diǎn)繞,其實(shí)簡(jiǎn)而言之,這個(gè) invoke 方法有點(diǎn)相當(dāng)一個(gè)方法攔截器,可以在調(diào)用實(shí)際的方法前植入一些前期操作,比如當(dāng)我們調(diào)用下面的代碼時(shí)

Call<List<Contributor>> call = github.contributors("square", "retrofit");

此時(shí)在執(zhí)行 contributors 方法時(shí),會(huì)先調(diào)用 InvocationHandler 的 invoke方法,在這個(gè)方法中,Retrofit 會(huì)對(duì)方法中的注解通過反射進(jìn)行解析,其中會(huì)解析形參類型,返回類型等等,并將其組裝成一個(gè) Call 對(duì)象返回。其實(shí)如果你對(duì) Java 的 AOP 有過了解的話,那么這里的思想也是一樣的。

動(dòng)態(tài)代理相關(guān)內(nèi)容就先解釋到這里,接下來我們來重點(diǎn)看看 loadServiceMethod 方法

ServiceMethod<?> loadServiceMethod(Method method) {
    //檢查緩存中是否有可用的接口方法
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        //解析接口方法中注解
        result = ServiceMethod.parseAnnotations(this, method);
        //加入緩存
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

loadServiceMethod 做的事情并不復(fù)雜,就是判斷方法簽名是否緩存過,沒有則解析方法中的所有注解,并加入緩存,代碼如下

abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    //解析接口方法中的所有注解并組裝成一個(gè)請(qǐng)求工廠
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

    //獲取接口方法的返回類型
    Type returnType = method.getGenericReturnType();
    
    //省略一些判斷代碼...
    
                   //這里對(duì)各種參數(shù)進(jìn)行檢查,并拼裝成一個(gè) CallAdapted 對(duì)象
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

  abstract @Nullable T invoke(Object[] args);
}

從上面的代碼中我們了解到,ServiceMethod 是一個(gè)抽象類,內(nèi)部聲明了一個(gè)靜態(tài)的泛型方法 parseAnnotations ,其中通過 RequestFactory 的靜態(tài) parseAnnotations 方法解析方法中的所有注解,通過 HttpServiceMethod 的靜態(tài) parseAnnotations 方法將解析的各種參數(shù)拼裝成一個(gè) CallAdapted 對(duì)象返回。

這里說明一下
HttpServiceMethod 也是個(gè)抽象類并繼承自 ServiceMethod 類且實(shí)現(xiàn)了 invoke 方法。

簡(jiǎn)單的了解了上面的代碼,接下來我們?cè)谥鹨豢纯?RequestFactory.parseAnnotations 和 HttpServiceMethod.parseAnnotations 為我們做了什么

五、Request 的參數(shù)解析過程

RequestFactory.parseAnnotations 方法在將 retrofit 對(duì)象和 method 傳入后,會(huì)創(chuàng)建一個(gè) Builder 對(duì)象,主要作用就是賦值,如下

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

Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();//方法的注解數(shù)組
      this.parameterTypes = method.getGenericParameterTypes();//形參的類型數(shù)組
      this.parameterAnnotationsArray = method.getParameterAnnotations();//形參的注解數(shù)組
}

而具體的解析則在 build 方法中進(jìn)行,這里在特別說明一下,我們知道 Retrofit 主要通過注解來解釋一個(gè)接口信息,這里我將 Retrofit 常用的的注解分為兩大類,一種是針對(duì)方法修飾,一種是針對(duì)參數(shù)進(jìn)行修飾,具體信息如下

method parameter
GET Url
POST Path
HEAD Query
PATCH QueryName
PUT QueryMap
OPTIONS Header
HTTP HeaderMap
Headers Field
Multipart FieldMap
FormUrlEncoded Part
- PartMap
- Body
- tag

知道了區(qū)別,我們來看一下 RequestFactory 的結(jié)構(gòu)圖以及解析的流程圖。


解析流程圖

可以看到解析的過程主要由兩個(gè)主要方法構(gòu)成,一個(gè)是 parseMethodAnnotation, 負(fù)責(zé)解析請(qǐng)求方法和 Header , 代碼很簡(jiǎn)單就不貼了,這里我們重點(diǎn)看一下 parseParameterAnnotation 方法,流程圖中寫的很清楚,它的主要作用就是將解析的形參注解生成一個(gè) ParameterHandler對(duì)象返回,那么這個(gè) ParameterHandler 能做些什么呢 ? 來看看它的結(jié)構(gòu)


ParameteHandler 繼承關(guān)系

可以看到 ParameterHandler 的子類共有 15 個(gè),且與上述的參數(shù)注解一一對(duì)應(yīng),其內(nèi)部有一個(gè)抽象方法 apply, 兩個(gè)默認(rèn)方法 iterable、array、其中前者主要解析的是集合數(shù)據(jù),后者主要針對(duì)數(shù)組的解析,但它們最終還是會(huì)調(diào)的是由各個(gè)子類實(shí)現(xiàn)的 apply 方法來將解析的鍵值對(duì)或 body 添加到我們的 request 中。

代碼形式如下

abstract class ParameterHandler<T> {
  abstract void apply(RequestBuilder builder, @Nullable T value) throws IOException;

  final ParameterHandler<Iterable<T>> iterable() {
    return new ParameterHandler<Iterable<T>>() {
      @Override void apply(RequestBuilder builder, @Nullable Iterable<T> values)
          throws IOException {
        if (values == null) return; // Skip null values.

        for (T value : values) {
          ParameterHandler.this.apply(builder, value);
        }
      }
    };
  }

  final ParameterHandler<Object> array() {
    return new ParameterHandler<Object>() {
      @Override void apply(RequestBuilder builder, @Nullable Object values) throws IOException {
        if (values == null) return; // Skip null values.

        for (int i = 0, size = Array.getLength(values); i < size; i++) {
          //noinspection unchecked
          ParameterHandler.this.apply(builder, (T) Array.get(values, i));
        }
      }
    };
  }

其實(shí)關(guān)于解析的具體代碼大家稍微耐心看看就會(huì)明白,基本過程都相同,筆者在這里就不浪費(fèi)太多筆墨了。

六、Call 對(duì)象的誕生

當(dāng)我們調(diào)用 API 接口時(shí),會(huì)返回一個(gè) call 對(duì)象來讓我們進(jìn)行 async/enqueue 請(qǐng)求,而整個(gè) Call 對(duì)象的生成其實(shí)是一個(gè)挺復(fù)雜的過程,這里筆者會(huì)分三小節(jié)來詳細(xì)展開這個(gè)過程。

6.1、HttpServiceMethod.parseAnnotations 的作用

接口的方法注解和參數(shù)注解解析完成后,我們就獲得了一個(gè) RequestFactory 對(duì)象,這個(gè)對(duì)象包含我們請(qǐng)求的所有信息,那么接下來看看 HttpServiceMethod.parseAnnotations 方法幫我門做了什么

abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
  
 
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
    boolean continuationWantsResponse = false;
    boolean continuationBodyNullable = false;

    Annotation[] annotations = method.getAnnotations();
    Type adapterType;
    
    //獲取要適配的 call 類型
    adapterType = method.getGenericReturnType();
   

    //創(chuàng)建 call 適配器,這個(gè)適配器的主要作用就是為了獲取要返回的類型和要執(zhí)行的回調(diào)線程
    //如果是協(xié)程則回調(diào)線程返回 null
    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    Type responseType = callAdapter.responseType();
    

    //這里就是創(chuàng)建轉(zhuǎn)換器的地方,內(nèi)部會(huì)通過上面的獲取的 responseType 創(chuàng)建合適的轉(zhuǎn)換器
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);

    //獲取 OkHttpClient 實(shí)例
    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if (!isKotlinSuspendFunction) {
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } 
  }
    
}

代碼做了精簡(jiǎn),刪除了 Kotlin 相關(guān)的部分,后面會(huì)單獨(dú)講,這里先主要講非 Kotlin 部分。

可以看到整個(gè)方法的作用就是為了返回一個(gè) CallAdapted 對(duì)象,而創(chuàng)建的過程由以下步驟完成

  • method.getGenericReturnType(); 獲取我們聲明的返回類型 Call<T>
  • createCallAdapter 創(chuàng)建一個(gè) Callback 線程
  • createResponseConverter 創(chuàng)建 response 的 converter
  • 獲取 Retrofit 初始化時(shí)創(chuàng)建的 OkHttpclient 對(duì)象
  • 組裝成一個(gè) CallAdapted 對(duì)象返回

如果光看 CallAdapted 這個(gè)名字的話是很有迷惑性的,你可能覺得會(huì)和 CallAdapter 有關(guān)系,又或者覺得這可能就是 Call 對(duì)象,這里筆者先賣個(gè)關(guān)子,后面會(huì)深入剖析,大家先記住就行。

接下來我們深入了解 CallAdapter 和 Converter 的創(chuàng)建過程

6.1、CallAdapter 的創(chuàng)建

跟進(jìn)createCallAdapter 方法后會(huì)發(fā)現(xiàn)經(jīng)過層層調(diào)用,最終會(huì)進(jìn)入到 Retorift 的 nextCallAdapter 方法中進(jìn)行實(shí)際的創(chuàng)建過程

 public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
      
    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;
      }
    }
  }

這是創(chuàng)建的 CallAdapter 的核心方法,代碼很簡(jiǎn)單就是開了個(gè)循環(huán),遍歷 callAdapterFactories 集合獲取合適的 callAdapter , (如果不知道 callAdapterFactories 集合從哪來的,請(qǐng)看第三節(jié)),如何才叫合適呢? 很簡(jiǎn)單就是根據(jù)我們的返回類型,對(duì)應(yīng)關(guān)系如下

returnType CallAdapter
Call.class DefaultCallAdapterFactory
Observable.class RxJavaCallAdapterFactory

我們目前一直使用的返回類型是 Call.class 所以上述循環(huán)中調(diào)用的其實(shí)是DefaultCallAdapterFactory 的 get 方法

 @Override public @Nullable CallAdapter<?, ?> get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
    //非 Call 類型直接返回 null,遍歷下一個(gè)
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    //必須為參數(shù)化類型,否則拋異常
    if (!(returnType instanceof ParameterizedType)) {
      throw new IllegalArgumentException(
          "Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
    }
    final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
    //這里其實(shí)主要的作用是為了判斷是否使用了協(xié)程,如果實(shí)現(xiàn)了協(xié)程那么則不使用系統(tǒng)的回調(diào)線程并返回null
    final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
        ? null
        : callbackExecutor;



    //創(chuàng)建一個(gè)匿名 CallAdapter 實(shí)例對(duì)象返回
    return new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public Call<Object> adapt(Call<Object> call) {
        return executor == null
            ? call
            : new ExecutorCallbackCall<>(executor, call);
      }
    };
  }

get 方法的還是很簡(jiǎn)單的就是創(chuàng)建了一個(gè)匿名的 CallAdapter 類并返回,這里你需要的就是重點(diǎn)記一下 CallAdapter的adapt 方法,因?yàn)楹竺鏁?huì)調(diào)用到,至于 callbackExecutor 內(nèi)部則是通過 Handler 將我們的請(qǐng)求結(jié)果切換到主線程。你看下 Platform Android 環(huán)境下的 defaultCallbackExecutor 方法就明白了。至于 ExecutorCallbackCall 其實(shí)是真正執(zhí)行請(qǐng)求的類,這里大家先了解下,后面會(huì)講到

Retrofit 常用的 CallAdapter 整體的結(jié)構(gòu)圖如下


image
6.2、ResponseConverter 的創(chuàng)建

ResponseConverter 的調(diào)用過程可以說基本和 CallAdapter 的調(diào)用邏輯一模一樣,也是層層調(diào)用,最終進(jìn)入到 nextResponseBodyConverter 方法中

 public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
      @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
      
    int start = converterFactories.indexOf(skipPast) + 1;
    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;
      }
    }
  }

和 callAdapter 的創(chuàng)建過程如出一轍,至于獲取什么類型的 converter 其實(shí)也有一個(gè)對(duì)應(yīng)關(guān)系

returnType converter
ResponseBody.class BuiltInConverters
Model.class GsonConverterFactory
Optional.class OptionalConverterFactory

顯然我們的例子只有 GsonConverterFactory 符合,所以進(jìn)入 responseBodyConverter 方法看看

  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonResponseBodyConverter<>(gson, adapter);
  }

代碼很簡(jiǎn)單就是通過 Gson 幫我們獲取要適配的返回類型,然后返回一個(gè) GsonResponseBodyConverter 對(duì)象,

而 GsonResponseBodyConverter 做的事情也很簡(jiǎn)單,通過重寫的 convert 方法將 RequestBody 中的流數(shù)據(jù)讀取并轉(zhuǎn)為我們的聲明的 model 類型,
代碼如下

final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
  private final Gson gson;
  @Override public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
   
      T result = adapter.read(jsonReader);
      if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
        throw new JsonIOException("JSON document was not fully consumed.");
      }
      return result;
  }
}

所以 GsonConverterFactory.responseBodyConverter 方法的主要作用就是幫我們創(chuàng)建了一個(gè) GsonResponseBodyConverter 對(duì)象來幫助我們完成 Response 的轉(zhuǎn)換

至此你應(yīng)該對(duì) Retrofit 的 Convert 工作流程有了一個(gè)比較清晰的了解,其實(shí) Retrofit 在構(gòu)建的時(shí)候內(nèi)部為我們提供了兩個(gè)默認(rèn)的 converter 分別是 BuiltInConverters 和 OptionalConverterFactory 后者只能在 Java8+ 或 Android API 24+ 環(huán)境中使用,且返回類型必須為 Optional.class 類型,感興趣的可以自己了解下,Android 基本用不到它。而前者則是通過 I/O 流 的方式將請(qǐng)求的信息和響應(yīng)信息包裝成一個(gè) RequestBody 和 ResponseBody ,所以當(dāng)你把返回類型設(shè)置為 Call<ResponseBody> 時(shí)也可以正常請(qǐng)求,但返回的是最原始的字節(jié)流。雖然每個(gè) Convert 的實(shí)現(xiàn)過程可能不盡相同,但基本的邏輯也是按照這個(gè)規(guī)律進(jìn)行的。

Retrofit 常用的 Converter 整體的結(jié)構(gòu)圖如下


image
6.3、 Call 對(duì)象的創(chuàng)建

經(jīng)過前面的分析,注解解析完了,OkHttpClient 對(duì)象有了,callAdapter 和 Converter 也創(chuàng)建了,至此終于滿足了創(chuàng)建 Call 對(duì)象的條件,但只是滿足了條件,此時(shí)還并未創(chuàng)建 Call 對(duì)象。而是將上述條件組裝成了一個(gè) CallAdapted 對(duì)象,看名字難道又是一個(gè) callAdapter 的子類? 可是我們前面不是已經(jīng)創(chuàng)建過了嗎?要想解除疑惑,讓我們看看 CallAdapted 的結(jié)構(gòu)圖就知道了


image

了解了 CallAdapted 的結(jié)構(gòu)關(guān)系,我們進(jìn)入代碼看看

 static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
    private final CallAdapter<ResponseT, ReturnT> callAdapter;

    CallAdapted(RequestFactory requestFactory, okhttp3.Call.Factory callFactory,
        Converter<ResponseBody, ResponseT> responseConverter,
        CallAdapter<ResponseT, ReturnT> callAdapter) {
        //將解析的注解, OkHttpClient對(duì)象,以及創(chuàng)建的 call 和 converter 賦值給對(duì)應(yīng)的成員變量
      super(requestFactory, callFactory, responseConverter);
      this.callAdapter = callAdapter;
    }

    @Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
      return callAdapter.adapt(call);
    }
  }
  

可以看到 CallAdapted 作為 HttpServiceMethod 的子類,只重寫了 adapt 方法,并調(diào)用了 callAdapter 的 adapt 方法并返回了一個(gè) RetrunT 類型的對(duì)象,那么這個(gè) RetrunT 是啥呢?

讓我們回歸使用的起點(diǎn) ,發(fā)起一個(gè)請(qǐng)求

Call<List<Contributor>> call = github.contributors("square", "retrofit");

開頭已經(jīng)大致講述了動(dòng)態(tài)代理的基本作用,這里不再贅述。

此時(shí)整個(gè)調(diào)用過程如下,這里大家也可以跟進(jìn)代碼按照以下步驟跟進(jìn)調(diào)用鏈

  • 通過動(dòng)態(tài)代理的方式調(diào)用 InvocationHandler 的 invoke 方法
  • 在 invoke 方法中調(diào)用 loadServiceMethod 方法解析接口的注解,根據(jù)返回類型創(chuàng)建對(duì)應(yīng)的 callAdapter 和 convert,組裝成一個(gè) CallAdapted 返回
  • 調(diào)用 CallAdapted 的 invoke 方法返回一個(gè) Call 對(duì)象

前面兩個(gè)步驟已經(jīng)做過詳細(xì)的分析,這里我們重點(diǎn)看下 invoke 方法。首先我們知道 CallAdapted 是 HttpServiceMethod 的子類,而 HttpServiceMethod 其實(shí)已經(jīng)實(shí)現(xiàn)了 invoke 方法,從前面的代碼中我們可以看到 CallAdapted 雖然只重寫了 adapt 方法,但根據(jù)繼承的特性 CallAdapted 自然也擁有了此方法。那么我們可以稍微改動(dòng)一下 CallAdpated 的代碼以方便大家理解

static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
    private final CallAdapter<ResponseT, ReturnT> callAdapter;

    @Override final @Nullable ReturnT invoke(Object[] args) {
        Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
        return adapt(call, args);
    }

    @Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
      return callAdapter.adapt(call);
    }
  }

可以看到在 invoke 方法中創(chuàng)建了一個(gè) OkHttpCall 對(duì)象,并將我們請(qǐng)求的信息,參數(shù)(就是 contributors("square", "retrofit") ),OkHttpClient(就是 callFactory), 和 converter 傳入。并調(diào)用了 adapt 方法, adapt 方法中又調(diào)用了 callAdapter 的 adapt 方法并將 OkHttpCall 對(duì)象傳入并返回。這一系列的調(diào)用過程到底發(fā)生了什么呢?

首先看看 OkHttpCall 的類結(jié)構(gòu)圖


image

如圖所示 OkHttpCall 的主要作用就是為了幫我們進(jìn)行 async / enqueue 請(qǐng)求。

而 callAdapter.adapt 從前面的分析中它其實(shí)調(diào)用的是 DefaultCallAdapterFactory 的 get 方法為我們創(chuàng)建的一個(gè)匿名 CallAdapter 對(duì)象中實(shí)現(xiàn)的 adapt 方法,代碼如下

final class DefaultCallAdapterFactory extends CallAdapter.Factory {
  private final @Nullable Executor callbackExecutor;
  @Override public @Nullable CallAdapter<?, ?> get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
   
    //這里其實(shí)主要的作用是為了判斷是否使用了協(xié)程,如果實(shí)現(xiàn)了協(xié)程那么則不使用系統(tǒng)的回調(diào)線程并返回null
    final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
        ? null
        : callbackExecutor; // 內(nèi)部調(diào)用 handler 切換到主線程
        
    return new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public Call<Object> adapt(Call<Object> call) {
        return executor == null
            ? call //OkHttoCall 對(duì)象
            : new ExecutorCallbackCall<>(executor, call); // 新建一個(gè) Call 對(duì)象,并將 OkHttpCall 對(duì)象傳入
      }
    };
  }

現(xiàn)在回過頭你可能就會(huì)明白我在 callAdapter 的創(chuàng)建那一節(jié)為什么要讓你重點(diǎn)關(guān)注 adapt 方法,因?yàn)?Call 對(duì)象就是在 adapt 方法中誕生的。而其中會(huì)涉及兩個(gè) Call 對(duì)象,一個(gè)是我們傳入的 OkHttpCall 另一個(gè)是 ExcutorCallBackCall 對(duì)象,而具體使用哪個(gè)則源于你是否用的是協(xié)程,如果是協(xié)程則直接用 OkHttpCall 否則創(chuàng)建一個(gè)新的 Call 對(duì)象。

那么此時(shí)再看這行代碼,相信你應(yīng)該知道我們的 Call 對(duì)象是如何誕生的吧,以及用的是哪個(gè) Call 對(duì)象了吧!

Call<List<Contributor>> call = github.contributors("square", "retrofit");

七、Call 對(duì)象的請(qǐng)求過程

讓我們先看一段 Retrofit 標(biāo)準(zhǔn)的請(qǐng)求過程

Call<List<Contributor>> call = github.contributors("square", "retrofit");

call.enqueue(new Callback<List<Contributor>>() {
        @Override
        public void onResponse(Call<List<Contributor>> call, Response<List<Contributor>> response) {
            for (Contributor contributor : contributors) {
                System.out.println(contributor.login + " (" + contributor.contributions + ")");
            }
        }

        @Override
        public void onFailure(Call<List<Contributor>> call, Throwable t) {

        }
});

經(jīng)過上一節(jié)的分析,我們知道了 Call 對(duì)象的誕生過程,也基本了解了 Call 的類結(jié)構(gòu),而在未使用協(xié)程的情況下我們使用的是新建的 ExecutorCallbackCall 對(duì)象,這個(gè) ExecutorCallbackCall 也實(shí)現(xiàn)了 Call 接口,所以它的結(jié)構(gòu)和 OkHttpCall 基本相同。讓我們看看它的 enqueue 方法

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

    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;// 主線程的 Handler 
      this.delegate = delegate; // 傳入的 OkHttpCall
    }

    @Override public void enqueue(final Callback<T> callback) {
      delegate.enqueue(new Callback<T>() {
        @Override public void onResponse(Call<T> call, final Response<T> response) {
          callbackExecutor.execute(() -> {
            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);
            }
          });
        }

        @Override public void onFailure(Call<T> call, final Throwable t) {
          callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
        }
      });
    }
}

代碼的邏輯并不復(fù)雜,很有意思的是 ExecutorCallbackCall 的 enqueue 方法最終又委托 OkHttpCall 的 enqueue 方法來進(jìn)行真正的請(qǐng)求,同理 async 請(qǐng)求也是如此??磥頍o論套幾層殼子,最終的活還是由 OkHttpCall 來執(zhí)行啊。

OkHttpCall 將請(qǐng)求的 Response 返回后,在經(jīng)由 callbackExecuter 將結(jié)果切換到主線程處理,這里再?gòu)?qiáng)調(diào)一下 callbackExecuter 對(duì)象是在 Retrofit 在構(gòu)建時(shí)根據(jù)系統(tǒng)環(huán)境自動(dòng)創(chuàng)建的,如果是 Android 環(huán)境下調(diào)用 defaultCallbackExecutor 方法會(huì)返回一個(gè) MainThreadExecutor 對(duì)象,代碼如下

 static final class Android extends Platform {
    Android() {
      super(Build.VERSION.SDK_INT >= 24);
    }

    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

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

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

看來要想了解真正的請(qǐng)求過程,還是得進(jìn)入到 OkHttpCall 中的 enqueue 中查看

@Override public void enqueue(final Callback<T> callback) {
    okhttp3.Call call;
    Throwable failure;

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      call = rawCall;
  
      if (call == null && failure == null) {
         call = rawCall = createRawCall();
      }
    }
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
       
        response = parseResponse(rawResponse);
      
        callback.onResponse(OkHttpCall.this, response);
       
      }

代碼進(jìn)行了精簡(jiǎn),我們只看核心代碼,其實(shí)整個(gè) enqueue 方法總結(jié)起來干了這么幾件事

  • createRawCall 創(chuàng)建一個(gè) OkHttp 原生的 Call 對(duì)象
  • 通過 OkHttp 的 Call 對(duì)象發(fā)起一個(gè) 異步請(qǐng)求
  • parseResponse 解析返回的 response
  • callback.onResponse() 將解析的 response 回調(diào)給 ExecutorCallbackCall 的 enqueue 方法

那就簡(jiǎn)單了,接下來我們就按照這個(gè)步驟來看看,首先是 createRawCall 方法

 private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    return call;
  }

原來是調(diào)用了 OkHttpClient 的 newCall 方法,而傳入的 request 是由 requestFactory 的 create 方法提供

okhttp3.Request create(Object[] args) throws IOException {
    @SuppressWarnings("unchecked") // 參數(shù)處理對(duì)象數(shù)組
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

    int argumentCount = args.length;
    //將接口注解解析的信息放入 RequestBuilder 中
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl,
        headers, contentType, hasBody, isFormEncoded, isMultipart);

    if (isKotlinSuspendFunction) {
      // The Continuation is the last parameter and the handlers array contains null at that index.
      argumentCount--;
    }

    //遍歷參數(shù)處理對(duì)象數(shù)組,通過 Apply 方法添加進(jìn) requestBuilder 
    List<Object> argumentList = new ArrayList<>(argumentCount);
    for (int p = 0; p < argumentCount; p++) {
      argumentList.add(args[p]);
      handlers[p].apply(requestBuilder, args[p]);
    }

    //返回一個(gè) request 對(duì)象
    return requestBuilder.get()
        .tag(Invocation.class, new Invocation(method, argumentList))
        .build();
  }

獲取完 request 后,就可以直接創(chuàng)建一個(gè)OkHttp 的 Call 對(duì)象了,接下來就是解析 Call 對(duì)象請(qǐng)求后的響應(yīng)了,進(jìn)入 parseResponse 方法看看

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();

    // 如果沒有響應(yīng)信息,這里創(chuàng)建一個(gè)沒有響應(yīng)體的原始 response 用于返回
    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();

    // 省略一些判斷代碼

    //創(chuàng)建一個(gè)用于捕獲在讀取響應(yīng)字節(jié)流過程發(fā)生 IO 異常的對(duì)象,內(nèi)部通過 Okio 讀取
    //該對(duì)象集成自 ResponseBody
    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
      //讀取正常,則通過 Converter 轉(zhuǎn)換我們的響應(yīng),并返回
      T body = responseConverter.convert(catchingBody);
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      // If the underlying source threw an exception, propagate that rather than indicating it was
      // a runtime exception.
      catchingBody.throwIfCaught();
      throw e;
    }
  }

我們例子返回的是 Call.class 類型,所以這里的 responseConverter 對(duì)應(yīng)的其實(shí)是 GsonResponseBodyConverter,而具體的轉(zhuǎn)換過程也是由 GsonResponseBodyConverter 的 convert 方法處理,代碼如下

final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
  private final Gson gson;
  private final TypeAdapter<T> adapter;
  
   GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
    this.gson = gson;// Gson 對(duì)象
    this.adapter = adapter;// model 的具體類型適配器
  }

  @Override public T convert(ResponseBody value) throws IOException {
    //讀取 ResponseBoyd 的字符流并創(chuàng)建一個(gè) JsonReader 對(duì)象
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      //通過類型適配器讀取 jsonReader 并轉(zhuǎn)轉(zhuǎn)換為我們聲明的 model 類型
      T result = adapter.read(jsonReader);
      if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
        throw new JsonIOException("JSON document was not fully consumed.");
      }
      return result;
    } finally {
      value.close();
    }
  }
}

解析完響應(yīng)返回后,最終調(diào)用 callback.onResponse 方法回調(diào)給 ExecutorCallbackCall ,在經(jīng)由 callbackExecutor 內(nèi)部的 handler 將 response 回調(diào)到主線程處理。

至此對(duì)于從 Retrofit 的構(gòu)建、到創(chuàng)建一個(gè) API 接口,以及發(fā)起一個(gè)請(qǐng)求的全部過程就分析完了。也完全對(duì)應(yīng)上了開頭的 Retorift 流程圖??v觀整個(gè)分析過程,真的感覺到設(shè)計(jì)這套框架的大佬的強(qiáng)大,能把各種設(shè)計(jì)模式,泛型、反射、注解靈活運(yùn)用成這個(gè)樣子,簡(jiǎn)直令人嘆服。真——五體投地,望塵莫及。

八、結(jié)語

限于篇幅,"Retrofit 源碼深入分析 —— Call 對(duì)象的誕生與請(qǐng)求" 就先分析到這里,如果想繼續(xù)了解 Retrofit 對(duì) RxJava 和 協(xié)程的支持,請(qǐng)看 Retrofit 源碼深入分析 —— RxJava 和 協(xié)程的支持

最后編輯于
?著作權(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)容