三方庫源碼筆記(7)- 超詳細的 Retrofit 源碼解析

對于 Android Developer 來說,很多開源庫都是屬于開發必備的知識點,從使用方式到實現原理再到源碼解析,這些都需要我們有一定程度的了解和運用能力。所以我打算來寫一系列關于開源庫源碼解析實戰演練的文章,初定的目標是 EventBus、ARouter、LeakCanary、Retrofit、Glide、OkHttp、Coil 等七個知名開源庫,希望對你有所幫助 ????

一、前言

Retrofit 也是現在 Android 應用開發中的標配之一了吧?筆者使用 Retrofit 蠻久的了,一直以來用著也挺舒心的,沒遇到啥大的坑。總這樣用著不來了解下其底層實現好像也不太好,趁著動筆寫 三方庫源碼筆記 系列文章就來對 Retrofit 進行一次(自我感覺的)全面的源碼解析吧 ~

Retrofit 是這么自我介紹的:A type-safe HTTP client for Android and Java. 這說明 Retrofit 的內部實現并不需要依賴于 Android 平臺,而是可以用于任意的 Java 客戶端,Retrofit 只是對 Android 平臺進行了特殊實現而已。此外,現在 Android 平臺的主流開發語言早已是 Kotlin 了,所以本篇文章所寫的例子都采用了 Kotlin ~

對 Kotlin 語言不熟悉的同學可以看我的這篇文章來入門:兩萬六千字帶你 Kotlin 入門

Retrofit 的源碼并不算太復雜,但由于應用了很多種設計模式,所以在流程上會比較繞。筆者陸陸續續看了幾天源碼后就開始動筆,但總感覺沒法闡述得特別清晰,寫著寫著就成了目前的樣子。讀者如果覺得我有哪里寫得不太好的地方也歡迎給下建議 ????

二、小例子

先來看幾個簡單的小例子,后續的講解都會圍繞這幾個例子來展開

dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
}

在除了 Retrofit 外不引入其它任何依賴庫的情況下,我們發起一個網絡請求的流程大致如下所示:

/**
 * @Author: leavesCZY
 * @Github:https://github.com/leavesCZY
 */
interface ApiService {

    @GET("getUserData")
    fun getUserData(): Call<ResponseBody>

}

fun main() {
    val retrofit = Retrofit.Builder()
        .baseUrl("https://mockapi.eolinker.com/9IiwI82f58c23411240ed608ceca204b2f185014507cbe3/")
        .build()
    val service = retrofit.create(ApiService::class.java)
    val call: Call<ResponseBody> = service.getUserData()
    call.enqueue(object : Callback<ResponseBody> {
        override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
            val userBean = response.body()?.string()
            println("userBean: $userBean")
        }

        override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
            println("onFailure: $t")
        }
    })
}

輸出結果:

userBean: {"userName":"JBl","userAge":7816977017260632}

Retrofit 是建立在 OkHttp 之上的一個網絡請求封裝庫,內部依靠 OkHttp 來完成實際的網絡請求。Retrofit 在使用上很簡潔,API 是通過 interface 來聲明的。不知道讀者第一次使用 Retrofit 的時候是什么感受,我第一次使用的時候就覺得 Retrofit 好神奇,我只需要通過 interface 來聲明 API 路徑、請求方式、請求參數、返回值類型等各個配置項,然后調用方法就可以發起網絡請求了,相比 OkHttp 和 Volley 這些網絡請求庫真的是簡潔到沒朋友

可以看到,getUserData()方法的請求結果是一個 Json 格式的字符串,其返回值類型被定義為 Call<ResponseBody>,此處的 ResponseBody 即 okhttp3.ResponseBody,是 OkHttp 提供的對網絡請求結果的包裝類,Call 即retrofit2.Call,是 Retrofit 對 okhttp3.Call做的一層包裝,OkHttp 在實際發起請求的時候使用的回調是okhttp3.Call,回調內部會中轉調用 retrofit2.Call,以便將請求結果轉交給外部

1、converter-gson

上述請求雖然簡單,但還不夠方便,因為既然 API 的返回值我們已知就是 Json 格式的了,那么我們自然就希望 getUserData() 方法的返回值直接就是一個 Bean 對象,而不是拿到一個 String 后還需要自己再去進行反序列化,這可以通過引入converter-gson這個庫來達到這個效果

dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
}

代碼再做點小改動,之后就可以直接在 Callback 中拿到 UserBean 對象了

/**
 * @Author: leavesCZY
 * @Github:https://github.com/leavesCZY
 */
interface ApiService {

    @GET("getUserData")
    fun getUserData(): Call<UserBean>

}

data class UserBean(val userName: String, val userAge: Long)

fun main() {
    val retrofit = Retrofit.Builder()
        .baseUrl("https://mockapi.eolinker.com/9IiwI82f58c23411240ed608ceca204b2f185014507cbe3/")
        .addConverterFactory(GsonConverterFactory.create())
        .build()
    val service = retrofit.create(ApiService::class.java)
    val call: Call<UserBean> = service.getUserData()
    call.enqueue(object : Callback<UserBean> {
        override fun onResponse(call: Call<UserBean>, response: Response<UserBean>) {
            val userBean = response.body()
            println("userBean: $userBean")
        }

        override fun onFailure(call: Call<UserBean>, t: Throwable) {
            println("onFailure: $t")
        }
    })
}

2、adapter-rxjava2

再然后,如果也看 Call<UserBean>不爽,想要通過 RxJava 的方式來進行網絡請求可不可以?也行,此時就需要再引入adapter-rxjava2這個庫了

dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'
}

代碼再來做點小改動,此時就完全不用使用到Call.enqueue來顯式發起網絡請求了,在進行 subscribe 的時候就會自動發起網絡請求

/**
 * @Author: leavesCZY
 * @Github:https://github.com/leavesCZY
 */
interface ApiService {

    @GET("getUserData")
    fun getUserData(): Observable<UserBean>

}

data class UserBean(val userName: String, val userAge: Long)

fun main() {
    val retrofit = Retrofit.Builder()
        .baseUrl("https://mockapi.eolinker.com/9IiwI82f58c23411240ed608ceca204b2f185014507cbe3/")
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .build()
    val service = retrofit.create(ApiService::class.java)
    val call: Observable<UserBean> = service.getUserData()
    call.subscribe(object : Consumer<UserBean> {
        override fun accept(userBean: UserBean?) {
            println("userBean: $userBean")
        }

    }, object : Consumer<Throwable> {
        override fun accept(t: Throwable?) {
            println("onFailure: $t")
        }
    })
}

3、提出疑問

可以看到,Retrofit 在抽象程度上是很高的。不管是需要 Call 類還是 Observable 類型的包裝類,只需要添加不同的CallAdapterFactory即可,就算想返回 LiveData 類型都是可以實現的。也不管是需要 ResponseBody 還是具體的 Bean 對象,也只需要添加不同的 ConverterFactory 即可,就算網絡請求返回值是 XML 格式也可以進行映射解析

之后,我們就帶著幾個問題來逐步看 Retrofit 的源碼:

  1. Retrofit 是如何將 interface 內部的方法轉化為一個個實際的 GET、POST、DELETE 等各式各樣的網絡請求的呢?例如,Retrofit 是如何將 getUserData() 方法轉換為一個 OkHttp 的 GET 請求的呢?
  2. Retrofit 是如何將 API 的返回值映射為具體的 Bean 對象的呢?例如,ResponseBody 是如何映射為 UserBean 的呢?
  3. Retrofit 是如何抽象不同的接口方法的返回值包裝類的呢?例如,Call 是如何替換為 Observable 的呢?

三、Retrofit.create()

先來看下retrofit.create方法做了什么

public <T> T create(final Class<T> service) {
    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 {
                // If the method is a method from Object then defer to normal invocation.
                if (method.getDeclaringClass() == Object.class) {
                //如果外部調用的是 Object 中聲明的方法的話則直接調用
                //例如 toString()、hashCode() 等方法
                  return method.invoke(this, args);
                }
                args = args != null ? args : emptyArgs;
                //根據 method 是否默認方法來決定如何調用
                return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
              }
            });
  }

這里的重點就是Proxy.newProxyInstance所實現的動態代理模式了。通過動態代理,Retrofit 會將我們對 ApiService 的調用操作轉發給 InvocationHandler 來完成。Retrofit 在后續會通過反射拿到我們在聲明 getUserData()時標注的各個配置項,例如 API 路徑、請求方式、請求參數、返回值類型等各個信息,然后將這些配置項拼接為 OkHttp 的一個網絡請求。當我們調用了call.enqueue方法時,這個操作就會觸發 InvocationHandler 去發起 OkHttp 網絡請求了

Retrofit 會根據 method 是否是默認方法來決定如何調用,這里主要看loadServiceMethod(method)方法,該方法的主要邏輯是:

  1. 將每個代表接口方法的 method 對象轉換為 ServiceMethod 對象,該對象中就包含了接口方法的具體信息
  2. 因為單個接口方法可能會先后被調用多次,所以將構造出來的 ServiceMethod 對象緩存到 serviceMethodCache 中以實現復用
private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();  

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;
}

四、ServiceMethod

從上面可知,loadServiceMethod(method)方法返回的是一個 ServiceMethod 對象,從名字可以猜出來每個 ServiceMethod 對象就對應一個接口方法,其內部就包含了對接口方法的解析結果。loadServiceMethod(method).invoke(args) 這個操作就對應調用接口方法并傳遞網絡請求參數這個過程,即對應service.getUserData() 這個過程

ServiceMethod 是一個抽象類,僅包含一個抽象的 invoke(Object[] args)方法。ServiceMethod 使用到了工廠模式,由于網絡請求最終的請求方式可能是多樣化的,既可能是通過線程池來執行,也可能是通過 Kotlin 協程來執行,使用工廠模式的意義就在于可以將這種差異都隱藏在不同的 ServiceMethod 實現類中,而外部統一都是通過 parseAnnotations 方法來獲取 ServiceMethod 的實現類

parseAnnotations方法返回的 ServiceMethod 實際上是 HttpServiceMethod,所以重點就要來看 HttpServiceMethod.parseAnnotations方法返回的 HttpServiceMethod 具體是如何實現的,并是如何拼接出一個完整的 OkHttp 請求調用鏈

abstract class ServiceMethod<T> {
    
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    //requestFactory 包含了對 API 的注解信息進行解析后的結果
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

    Type returnType = method.getGenericReturnType();
    //如果返回值包含未確定的泛型類型或者是包含通配符的話,那么就拋出異常
    //因為 Retrofit 無法構造出一個不具有確定類型的對象作為返回值
    if (Utils.hasUnresolvableType(returnType)) {
      throw methodError(
          method,
          "Method return type must not include a type variable or wildcard: %s",
          returnType);
    }
    //返回值類型不能是 void
    if (returnType == void.class) {
      throw methodError(method, "Service methods cannot return void.");
    }
    
    //重點
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

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

五、HttpServiceMethod

ServiceMethod 這個抽象類的直接子類只有一個,即 HttpServiceMethod。HttpServiceMethod 也是一個抽象類,其包含兩個泛型聲明,ResponseT 表示的是接口方法返回值的外層包裝類型,ReturnT 表示的是我們實際需要的數據類型。例如,對于 fun getUserData(): Call<UserBean> 方法,ResponseT 對應的是 Call,ReturnT 對應的是 UserBean

HttpServiceMethod 實現了父類的 invoke 方法,并將操作轉交給了另一個抽象方法 adapt 來完成。可以看到,即使我們為接口方法聲明的返回值類型是 Observable<UserBean>invoke 方法內部其實還是需要創建出一個 Call 對象的,HttpServiceMethod 只是把 Call 轉換為 Observable 的這個過程交由了 adapt 方法來完成

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

  protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);
    
  ···

}

再來看HttpServiceMethod.parseAnnotations()方法是如何構建出一個 HttpServiceMethod 對象的,并且該對象的adapt方法是如何實現的

abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {

  static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    //是否是 Suspend 函數,即是否以 Kotlin 協程的方式來進行請求
    boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
    boolean continuationWantsResponse = false;
    boolean continuationBodyNullable = false;

    Annotation[] annotations = method.getAnnotations();
    Type adapterType;
    if (isKotlinSuspendFunction) {
        //省略 Kotlin 協程的一些處理邏輯
    } else {
      adapterType = method.getGenericReturnType();
    }
    
    //重點1
    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
      
    //拿到包裝類內部的具體類型,例如,Observable<UserBean> 內部的 UserBean
    //responseType 不能是 okhttp3.Response 或者是不包含具體泛型類型的 Response
    Type responseType = callAdapter.responseType();
    if (responseType == okhttp3.Response.class) {
      throw methodError(
          method,
          "'"
              + getRawType(responseType).getName()
              + "' is not a valid response body type. Did you mean ResponseBody?");
    }
    if (responseType == Response.class) {
      throw methodError(method, "Response must include generic type (e.g., Response<String>)");
    }
    // TODO support Unit for Kotlin?
    if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
      throw methodError(method, "HEAD method must use Void as response type.");
    }
    
    //重點2
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);

    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if (!isKotlinSuspendFunction) {
      //重點3
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } 
    
    //省略 Kotlin 協程的一些處理邏輯
    ···
        
  }

  ···
    
}

Retrofit 目前已經支持以 Kotlin 協程的方式來進行調用了,但本例子和協程無關,所以此處先忽略協程相關的處理邏輯,后面會再講解,parseAnnotations 方法的主要邏輯是:

  1. 先通過createCallAdapter(retrofit, method, adapterType, annotations 方法拿到 CallAdapter 對象,CallAdapter 就用于實現接口方法的返回值包裝類處理邏輯。例如,getUserData()方法的返回值包裝類類型如果是 Call ,那么返回的 CallAdapter 對象就對應 DefaultCallAdapterFactory 包含的 Adapter;如果是 Observable,那么返回的就是 RxJava2CallAdapterFactory 包含的 Adapter
  2. 再通過 createResponseConverter(retrofit, method, responseType)方法拿到 Converter 對象,Converter 就用于實現接口方法的返回值處理邏輯。例如,getUserData()方法的目標返回值類型如果是 ResponseBody,那么 Converter 對象就對應 BuiltInConverters;如果是 UserBean,那么就對應 GsonConverterFactory
  3. 根據前兩個步驟拿到的值,構造出一個 CallAdapted 對象并返回

CallAdapted 正是 HttpServiceMethod 的子類,在以上步驟中已經找到了可以實現將 Call 轉換為 Observable 的 CallAdapter 了,所以對于 CallAdapted 來說,其 adapt 方法會直接將 Call 提交給 CallAdapter,由其去實現這種轉換過程

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

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) {
      super(requestFactory, callFactory, responseConverter);
      this.callAdapter = callAdapter;
    }

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

六、OkHttpCall

OkHttpCall 是實際發起 OkHttp 請求的地方。當我們調用 fun getUserData(): Call<ResponseBody> 方法時,返回的 Call 對象實際上是 OkHttpCall 類型,而當我們調用 call.enqueue(Callback)方法時,enqueue 方法就會發起一個 OkHttp 請求,傳入的 retrofit2.Callback 對象就會由 okhttp3.Callback本身收到回調時再進行中轉調用

final class OkHttpCall<T> implements Call<T> {
  private final RequestFactory requestFactory;
  private final Object[] args;
  private final okhttp3.Call.Factory callFactory;
  private final Converter<ResponseBody, T> responseConverter;

  private volatile boolean canceled;

  @GuardedBy("this")
  private @Nullable okhttp3.Call rawCall;

  @GuardedBy("this") // Either a RuntimeException, non-fatal Error, or IOException.
  private @Nullable Throwable creationFailure;

  @GuardedBy("this")
  private boolean executed;
    
  @Override
  public void enqueue(final Callback<T> callback) { 
    ···
    okhttp3.Call call;
    ··· 
    call.enqueue( new okhttp3.Callback() {
          @Override
          public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
            Response<T> response;
            try {
              response = parseResponse(rawResponse);
            } catch (Throwable e) {
              throwIfFatal(e);
              callFailure(e);
              return;
            }

            try {
              callback.onResponse(OkHttpCall.this, response);
            } catch (Throwable t) {
              throwIfFatal(t);
              t.printStackTrace(); // TODO this is not great
            }
          }

          @Override
          public void onFailure(okhttp3.Call call, IOException e) {
            callFailure(e);
          }

          private void callFailure(Throwable e) {
            try {
              callback.onFailure(OkHttpCall.this, e);
            } catch (Throwable t) {
              throwIfFatal(t);
              t.printStackTrace(); // TODO this is not great
            }
          }
        });
  }
 
   ···
    
}

七、做個總結

以上幾個小節的內容講了在發起如下請求的過程中涉及到的所有流程,但單純這樣看的話其實有點難把握各個小點,我自己看著都有點繞,所以這里就再來回顧下以上內容,把所有知識點給串聯起來

  • 首先,我們通過 retrofit.create(ApiService::class.java)得到一個 ApiService 的動態實現類,這是通過 Java 原生提供的Proxy.newProxyInstance 代表的動態代理功能來實現的。在拿到 ApiService 的實現類后,我們就可以直接調用 ApiService 中聲明的所有方法了
  • 當我們調用了service.getUserData()方法時,Retrofit 會將每一個接口方法都抽象封裝為一個 ServiceMethod 對象并緩存起來,我們的操作會轉交給 ServiceMethod 來完成,由 ServiceMethod 來負責返回我們的目標類型,對應的是 serviceMethod.invoke(Object[] args)方法,args 代表的是我們調用接口方法時需要傳遞的參數,對應本例子就是一個空數組
  • ServiceMethod 使用到了工廠模式,由于網絡請求最終的請求方式可能是多樣化的,既可能是通過線程池來執行,也可能是通過 Kotlin 協程來執行,使用工廠模式的意義就在于可以將這種差異都隱藏在不同的 ServiceMethod 實現類中,而外部統一都是通過 parseAnnotations 方法來獲取 ServiceMethod 的實現類
  • ServiceMethod 具有一個唯一的直接子類,即 HttpServiceMethod。HttpServiceMethod 自身已經找到了可以將 Call 轉換為 Observable,ResponseBody 轉換為 UserBean 的轉換器,其invoke方法會構建出一個 OkHttpCall 對象,然后轉發給抽象方法adapt,由adapt來發起實際的網絡請求
  • 而不管外部的接口方法返回值類型是不是 Observable<UserBean>,最終的網絡請求都是需要通過 OkHttpCall 來發起,HttpServiceMethod 依靠找到的轉換器將 OkHttpCall 給隱藏在了內部

八、接口方法是如何解析的?

Retrofit 是如何將 interface 內部的方法轉化為一個個實際的 GET、POST、DELETE 等各式各樣的網絡請求的呢?例如,Retrofit 是如何將 getUserData() 轉換為一個 OkHttp 的 GET 請求的呢?

這個過程在 ServiceMethod 的 parseAnnotations 方法中就已經完成的了,對應的是 RequestFactory.parseAnnotations(retrofit, method)方法

abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    //重點
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
    ···
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

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

前文說了,Retrofit 是建立在 OkHttp 之上的一個網絡請求封裝庫,內部依靠 OkHttp 來完成實際的網絡請求。而 OkHttp 的一般請求方式如下所示

fun run(url: String): String {
    val request: Request = Request.Builder()
        .url(url)
        .build()
    OkHttpClient().newCall(request).execute().use { response ->
        return response.body!!.string()
    }
}

OkHttp 需要構建一個 Request 對象來配置請求方式和請求參數,以此來發起網絡請求。所以,Retrofit 也需要一個構建 Request 對象的過程,這個過程就隱藏在 RequestFactory 中

RequestFactory 采用了 Builder 模式,這里無需過多理會其構建過程,我們只要知道 RequestFactory 中包含了對 API 方法的各項解析結果即可。其 create(Object[] args)方法就會根據各項解析結果,最終返回一個 okhttp3.Request 對象

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

  private final Method method;
  private final HttpUrl baseUrl;
  final String httpMethod;
  private final @Nullable String relativeUrl;
  private final @Nullable Headers headers;
  private final @Nullable MediaType contentType;
  private final boolean hasBody;
  private final boolean isFormEncoded;
  private final boolean isMultipart;
  private final ParameterHandler<?>[] parameterHandlers;
  final boolean isKotlinSuspendFunction;

  okhttp3.Request create(Object[] args) throws IOException {
    @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

    int argumentCount = args.length;
    if (argumentCount != handlers.length) {
      throw new IllegalArgumentException(
          "Argument count ("
              + argumentCount
              + ") doesn't match expected count ("
              + handlers.length
              + ")");
    }

    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--;
    }

    List<Object> argumentList = new ArrayList<>(argumentCount);
    for (int p = 0; p < argumentCount; p++) {
      argumentList.add(args[p]);
      handlers[p].apply(requestBuilder, args[p]);
    }

    return requestBuilder.get().tag(Invocation.class, new Invocation(method, argumentList)).build();
  }
    
}

我們現在知道,OkHttpCall 是實際上發起網絡請求的地方,所以最終 RequestFactory 的 create 方法會由 OkHttpCall 的 createRawCall() 方法來調用

final class OkHttpCall<T> implements Call<T> {
     
    private okhttp3.Call createRawCall() throws IOException {
        okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
        if (call == null) {
            throw new NullPointerException("Call.Factory returned null.");
        }
        return call;
    }
     
}

九、ResponseBody 如何映射為 UserBean

Retrofit 是如何將 API 的返回值映射為具體的 Bean 對象的呢?例如,ResponseBody 是如何映射為 UserBean 的呢?

OkHttp 默認的接口返回值對象是 ResponseBody,如果不引入converter-gson,我們只能將接口請求結果都定義為 ResponseBody,而不能是具體的 Bean 對象,因為 Retrofit 無法自動地完成 ResponseBody 到 UserBean 之間的轉換操作,需要我們將這種轉換規則告知 Retrofit。這種轉換規則被 Retrofit 定義為 Converter 接口,對應它的 responseBodyConverter方法

public interface Converter<F, T> {
    
  @Nullable
  T convert(F value) throws IOException;

  abstract class Factory {
    
    //將 ResponseBody 轉換為目標類型 type
    public @Nullable Converter<ResponseBody, ?> responseBodyConverter(
        Type type, Annotation[] annotations, Retrofit retrofit) {
      return null;
    }

    ···
  }
}

為了能直接獲取到 UserBean 對象,我們需要在構建 Retrofit 對象的時候添加 GsonConverterFactory。GsonConverterFactory 會根據目標類型 type,通過 Gson 來進行反序列化出 UserBean 對象

public final class GsonConverterFactory extends Converter.Factory {

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

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;
    this.adapter = adapter;
  }

  @Override public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      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();
    }
  }
}

那么,問題又來了,Retrofit 是如何知道什么類型才可以交由 GsonConverterFactory 來進行處理的呢?至少 ResponseBody 就不應該交由 GsonConverterFactory 來處理,Retrofit 如何進行選擇呢?

首先,當我們在構建 Retrofit 對象時傳入了 GsonConverterFactory,最終 Retrofit 會對所有 Converter.Factory 進行排序,converterFactories 中 BuiltInConverters 會被默認排在第一位,BuiltInConverters 是 Retrofit 自帶的對 ResponseBody 進行默認解析的 Converter.Factory 實現類

public final class Retrofit {
    
   public static final class Builder {
          public Retrofit build() {
      
      ···
              
      // Make a defensive copy of the converters.
      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.
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      converterFactories.addAll(platform.defaultConverterFactories());

      ···
   }
   
}

而 BuiltInConverters 的 responseBodyConverter 方法在目標類型并非 ResponseBody、Void、Unit 等三種類型的情況下會返回 null

final class BuiltInConverters extends Converter.Factory {
    
@Override
public @Nullable Converter<ResponseBody, ?> responseBodyConverter(
      Type type, Annotation[] annotations, Retrofit retrofit) {
    if (type == ResponseBody.class) {
      return Utils.isAnnotationPresent(annotations, Streaming.class)
          ? StreamingResponseBodyConverter.INSTANCE
          : BufferingResponseBodyConverter.INSTANCE;
    }
    if (type == Void.class) {
      return VoidResponseBodyConverter.INSTANCE;
    }
    if (checkForKotlinUnit) {
      try {
        if (type == Unit.class) {
          return UnitResponseBodyConverter.INSTANCE;
        }
      } catch (NoClassDefFoundError ignored) {
        checkForKotlinUnit = false;
      }
    }
    return null;
  }

  ···

}

而 Retrofit 類的 nextResponseBodyConverter 方法就是為每一個接口方法選擇 Converter 進行返回值數據類型轉換的方法。該方法會先遍歷到 BuiltInConverters,發現其返回了 null,就會最終選擇到 GsonResponseBodyConverter,從而完成數據解析。如果最終沒有找到一個合適的處理器的話,就會拋出 IllegalArgumentException

public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
      @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
    Objects.requireNonNull(type, "type == null");
    Objects.requireNonNull(annotations, "annotations == null");

    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;
      }
    }
    ···
    throw new IllegalArgumentException(builder.toString());
}

十、Call 如何替換為 Observable

Retrofit 是如何抽象不同的接口返回值包裝類的呢?例如,Call 是如何替換為 Observable 的?

與上一節內容相類似,Retrofit 在默認情況下也只支持將 retrofit2.Call 作為接口方法的返回值包裝類,為了支持返回 Observable 類型,我們需要在構建 Retrofit 的時候添加 RxJava2CallAdapterFactory

Retrofit 將retrofit2.Call轉換為Observable的這種規則抽象為了 CallAdapter 接口

public interface CallAdapter<R, T> {

  //返回具體的內部類型,即 UserBean
  Type responseType();

  //用于將 Call 轉換為 Observable
  T adapt(Call<R> call);

  abstract class Factory {

    //用于提供將 Call<UserBean> 轉換為 Observable<UserBean> 的 CallAdapter 對象
    //此處的 returnType 即 Observable<UserBean>
    //如果此 CallAdapter 無法完成這種數據類型的轉換,那么就返回 null
    public abstract @Nullable CallAdapter<?, ?> get(
        Type returnType, Annotation[] annotations, Retrofit retrofit);

    ···
        
  }
}

對于 RxJava2CallAdapterFactory 的 get 方法而言,如何返回值類型并非 Completable、Flowable、Single、Maybe 等類型的話就會返回 null,否則就返回 RxJava2CallAdapter 對象

public final class RxJava2CallAdapterFactory extends CallAdapter.Factory {
    
  ···
  
  @Override
  public @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    
        Class<?> rawType = getRawType(returnType);
        if (rawType == Completable.class) {
            // Completable is not parameterized (which is what the rest of this method deals with) so it
            // can only be created with a single configuration.
            return new RxJava2CallAdapter(
            Void.class, scheduler, isAsync, false, true, false, false, false, true);
        }

        boolean isFlowable = rawType == Flowable.class;
        boolean isSingle = rawType == Single.class;
        boolean isMaybe = rawType == Maybe.class;
        if (rawType != Observable.class && !isFlowable && !isSingle && !isMaybe) {
            return null;
        }
        ···
      
        return new RxJava2CallAdapter(responseType, scheduler, isAsync, isResult, isBody, isFlowable, isSingle, isMaybe, false);
  }
}

對于本例子而言,最終 RxJava2CallAdapter 又會返回 CallExecuteObservable,CallExecuteObservable 又會在外部進行 subscribe 的時候調用 call.execute() 方法來發起網絡請求,所以在上面的例子中我們并不需要顯式地發起網絡請求,而是在進行 subscribe 的時候就自動觸發請求了,Observer 只需要等待網絡請求結果自動回調出來即可

final class RxJava2CallAdapter<R> implements CallAdapter<R, Object> {
  
  ···
        
  @Override
  public Type responseType() {
    return responseType;
  }

  @Override
  public Object adapt(Call<R> call) {
    Observable<Response<R>> responseObservable =
        isAsync ? new CallEnqueueObservable<>(call) : new CallExecuteObservable<>(call);

    ···
    return RxJavaPlugins.onAssembly(observable);
  }
}

final class CallExecuteObservable<T> extends Observable<Response<T>> {
  private final Call<T> originalCall;

  CallExecuteObservable(Call<T> originalCall) {
    this.originalCall = originalCall;
  }

  @Override
  protected void subscribeActual(Observer<? super Response<T>> observer) {
    // Since Call is a one-shot type, clone it for each new observer.
    Call<T> call = originalCall.clone();
    CallDisposable disposable = new CallDisposable(call);
    observer.onSubscribe(disposable);
    if (disposable.isDisposed()) {
      return;
    }

    boolean terminated = false;
    try {
      //發起網絡請求
      Response<T> response = call.execute();
      if (!disposable.isDisposed()) {
        //將請求結果傳給外部
        observer.onNext(response);
      }
      if (!disposable.isDisposed()) {
        terminated = true;
        observer.onComplete();
      }
    } catch (Throwable t) {
      Exceptions.throwIfFatal(t);
      if (terminated) {
        RxJavaPlugins.onError(t);
      } else if (!disposable.isDisposed()) {
        try {
          observer.onError(t);
        } catch (Throwable inner) {
          Exceptions.throwIfFatal(inner);
          RxJavaPlugins.onError(new CompositeException(t, inner));
        }
      }
    }
  }
    
  ···

}

那么,問題又來了,Retrofit 是如何知道什么類型才可以交由 RxJava2CallAdapterFactory 來進行處理的呢?

首先,當我們在構建 Retrofit 對象時傳入了 RxJava2CallAdapterFactory,最終 Retrofit 會按照添加順序對所有 CallAdapter.Factory 進行保存,且默認會在隊尾添加一個 DefaultCallAdapterFactory,用于對包裝類型為 retrofit2.Call的情況進行解析

而 Retrofit 類的 nextCallAdapter 方法就是為每一個 API 方法選擇 CallAdapter 進行返回值數據類型轉換的方法。該方法會先遍歷到 RxJava2CallAdapter ,發現其返回了非 null 值,之后就交由其進行處理

public CallAdapter<?, ?> nextCallAdapter(
      @Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {
    Objects.requireNonNull(returnType, "returnType == null");
    Objects.requireNonNull(annotations, "annotations == null");

    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;
      }
    }

    ···
        
    throw new IllegalArgumentException(builder.toString());
  }

十一、再總結下

這里再來總結下上面兩節關于 Retrofit 整個數據轉換的流程的內容

在默認情況下,我們從回調 Callback 中取到的最原始的返回值類型是 Response<ResponseBody>,而在引入了 converter-gsonadapter-rxjava2 之后,我們可以直接拿到目標類型 UserBean

Retrofit 為了達到這種轉換效果,就要先后進行兩個步驟:

  1. 將 ResponseBody 轉換為 UserBean,從而可以得到接口方法返回值 Response<UserBean>
  2. 將 Call 轉換為 Observable,Observable 直接從 Response<UserBean> 中把 UserBean 取出來作為返回值來返回,從而直接得到目標類型 UserBean

第一個步驟即第九節所講的內容,ResponseBody 轉為 UserBean 的轉換規則是通過 Converter 接口來定義的

public interface Converter<F, T> {
   
  //用于將 F 類型轉換為 T 類型
  @Nullable
  T convert(F value) throws IOException;

  ···
      
}

這個過程的轉換就發生在 OkHttpCall 中,enqueue 方法在拿到 OkHttp 返回的 okhttp3.Response 對象后,就通過調用 parseResponse方法來完成轉化為 Response<T>的邏輯,當中就調用了 Converter 接口的 convert 方法,從而得到返回值 Response<UserBean>

  final class OkHttpCall<T> implements Call<T> {
 
  @Override
  public void enqueue(final Callback<T> callback) {
    call.enqueue(
        new okhttp3.Callback() {
          @Override
          public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
            Response<T> response;
            try {
              //重點
              response = parseResponse(rawResponse);
            } catch (Throwable e) {
              throwIfFatal(e);
              callFailure(e);
              return;
            }

            try {
              callback.onResponse(OkHttpCall.this, response);
            } catch (Throwable t) {
              throwIfFatal(t);
              t.printStackTrace(); // TODO this is not great
            }
          }
        });
  }
  
 private final Converter<ResponseBody, T> responseConverter;
    
 Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ···
    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
      //catchingBody 就是 ResponseBody 類型,將其轉換為 T 類型
      T body = responseConverter.convert(catchingBody);
      //然后再將其包裝為 Response<T> 類型
      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 轉換為 Observable 的轉換規則是通過 CallAdapter 接口來定義的

public interface CallAdapter<R, T> {

  Type responseType();

  //此方法就用于將 Call<R> 轉為你希望的目標類型 T,例如:Observable<UserBean>
  T adapt(Call<R> call);

  ··· 
   
}

在 CallEnqueueObservable 這個類中,通過自定義回調接口 CallCallback 來發起網絡請求,從而拿到在第一個步驟解析完成后的數據,即 Response<UserBean> 對象

final class CallEnqueueObservable<T> extends Observable<Response<T>> {
  private final Call<T> originalCall;

  CallEnqueueObservable(Call<T> originalCall) {
    this.originalCall = originalCall;
  }

  @Override
  protected void subscribeActual(Observer<? super Response<T>> observer) {
    // Since Call is a one-shot type, clone it for each new observer.
    Call<T> call = originalCall.clone();
    CallCallback<T> callback = new CallCallback<>(call, observer);
    observer.onSubscribe(callback);
    if (!callback.isDisposed()) {
      //自定義回調,發起請求
      call.enqueue(callback);
    }
  }

  private static final class CallCallback<T> implements Disposable, Callback<T> {
    private final Call<?> call;
    private final Observer<? super Response<T>> observer;
    private volatile boolean disposed;
    boolean terminated = false;

    CallCallback(Call<?> call, Observer<? super Response<T>> observer) {
      this.call = call;
      this.observer = observer;
    }

    @Override
    public void onResponse(Call<T> call, Response<T> response) {
        ···
        //直接將Response<T>傳遞出去,即 Response<UserBean> 對象
        observer.onNext(response);
        ···
    }

    ···
  }
}

CallCallback 類同時持有著一個 observer 對象,該 observer 對象實際上又屬于 BodyObservable 類。BodyObservable 在拿到了 Response<UserBean> 對象后,如果判斷到此次網絡請求屬于成功狀態的話,那么就直接取出 body (即 UserBean)傳遞出去。因此我們才可以直接拿到目標類型,而不包含任何包裝類

final class BodyObservable<T> extends Observable<T> {
  private final Observable<Response<T>> upstream;

  BodyObservable(Observable<Response<T>> upstream) {
    this.upstream = upstream;
  }

  @Override
  protected void subscribeActual(Observer<? super T> observer) {
    upstream.subscribe(new BodyObserver<T>(observer));
  }

  private static class BodyObserver<R> implements Observer<Response<R>> {
    private final Observer<? super R> observer;
    private boolean terminated;

    BodyObserver(Observer<? super R> observer) {
      this.observer = observer;
    }

    @Override
    public void onSubscribe(Disposable disposable) {
      observer.onSubscribe(disposable);
    }

    @Override
    public void onNext(Response<R> response) {
      if (response.isSuccessful()) {
        //如果本次網絡請求成功,那么就直接取出 body 傳遞出去
        observer.onNext(response.body());
      } else {
        terminated = true;
        Throwable t = new HttpException(response);
        try {
          observer.onError(t);
        } catch (Throwable inner) {
          Exceptions.throwIfFatal(inner);
          RxJavaPlugins.onError(new CompositeException(t, inner));
        }
      }
    }

    ···
  }
}

十二、使用 Kotlin 協程

Retrofit 的當前版本已經支持以 Kotlin 協程的方式來調用了,這里就來看下 Retrofit 是如何支持協程調用的

先導入所有需要使用到的依賴,因為本例子是純 Kotlin 項目,所以就不導入 Android 平臺的 Kotlin 協程支持庫了

dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9'
}

本例子通過 runBlocking 來啟動一個協程,避免網絡請求還未結束 main 線程就停止了。需要注意的是,在實際開發中應該避免這樣來使用協程,否則使用協程就沒有多少意義了

/**
 * @Author: leavesCZY
 * @Github:https://github.com/leavesCZY
 */
interface ApiService {

    @GET("getUserData")
    suspend fun getUserData(): UserBean

}

data class UserBean(val userName: String, val userAge: Long)

fun main() {
    val retrofit = Retrofit.Builder()
        .baseUrl("https://mockapi.eolinker.com/9IiwI82f58c23411240ed608ceca204b2f185014507cbe3/")
        .addConverterFactory(GsonConverterFactory.create())
        .build()
    val service = retrofit.create(ApiService::class.java)
    runBlocking {
        val job: Job = launch {
            try {
                val userBean: UserBean = service.getUserData()
                println("userBean: $userBean")
            } catch (e: Throwable) {
                println("onFailure: $e")
            }
        }
    }
}

在本例子中,getUserData()方法的返回值就不需要任何包裝類了,我們直接聲明目標數據類型就可以了,在使用上會比之前更加簡潔方便

好了,開始來分析下流程

我們先為 ApiService 多聲明幾個方法,方便來分析規律。每個方法都使用 suspend關鍵字進行修飾,標明其只能用于在協程中來調用

interface ApiService {

    @GET("getUserData")
    fun getUserData(): UserBean

    @GET("getUserData")
    suspend fun getUserData1(): UserBean

    @GET("getUserData")
    suspend fun getUserData2(id: String): UserBean

    @GET("getUserData")
    suspend fun getUserData3(id: String, limit: Int): UserBean

}

Retrofit 是以 Java 語言實現的,但 suspend 關鍵字只能用于 Kotlin,兩者就存在著“溝通障礙”,但只要調用方也屬于 JVM 語言的話,那么按道理來說 Retrofit 就都是可以使用的,此處就通過 IDEA 將 ApiService 反編譯為了以下的 Java 類,看下 suspend 函數在 Retrofit 的角度來看是怎么實現的

public interface ApiService {
   @GET("getUserData")
   @NotNull
   UserBean getUserData();

   @GET("getUserData")
   @Nullable
   Object getUserData1(@NotNull Continuation var1);

   @GET("getUserData")
   @Nullable
   Object getUserData2(@NotNull String var1, @NotNull Continuation var2);

   @GET("getUserData")
   @Nullable
   Object getUserData3(@NotNull String var1, int var2, @NotNull Continuation var3);
}

可以看到,非 suspend 函數的轉換結果還符合我們的心理預期,但是 suspend 函數就相差得比較大了,方法返回值類型都變為 Object,且在方法的參數列表的最后都被添加了一個 kotlin.coroutines.Continuation 參數。這個參數是重點,后面會使用到

在 RequestFactory 類中包含一個 isKotlinSuspendFunction 的布爾變量,就用來標記當前解析到的 Method 是否是 suspend 函數。在 RequestFactory 的 build()方法中,會對 API 方法的每一個參數進行解析,當中就包含了檢測當前解析的參數是否是屬于最后一個參數的邏輯

RequestFactory build() {
      ···
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
        parameterHandlers[p] =
            //p == lastParameter 如果為 true 就說明當前解析的 parameterTypes[p] 是 API 方法的最后一個參數
            parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
      }
      ···
      return new RequestFactory(this);
}

如果檢測到最后一個參數類型就是 Continuation.class的話,那么 isKotlinSuspendFunction 就會變成 true。這個檢測邏輯就符合了上面所介紹的 Kotlin 類型的 ApiService 代碼轉換為 Java 形式后的變化規則

private @Nullable ParameterHandler<?> parseParameter(
        int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
      ···
      if (result == null) {
        if (allowContinuation) {
          try {
            if (Utils.getRawType(parameterType) == Continuation.class) {
              isKotlinSuspendFunction = true;
              return null;
            }
          } catch (NoClassDefFoundError ignored) {
          }
        }
        throw parameterError(method, p, "No Retrofit annotation found.");
      }

      return result;
    }

然后,在 HttpServiceMethod 的 parseAnnotations方法中我們就會用到isKotlinSuspendFunction這個變量

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;
    if (isKotlinSuspendFunction) {
      ···
      //雖然 getUserData() 方法我們直接定義返回類型為 UserBean
      //但實際上 Retrofit 還是需要將返回類型轉為 Call<UserBean>,使之符合我們上述的數據解析流程
      //所以,此處的 responseType 為 UserBean,adapterType 確是 Call<UserBean>
      adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
      annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
    } else {
      adapterType = method.getGenericReturnType();
    }

    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    Type responseType = callAdapter.responseType();
    
    ···
        
    //重點
      return (HttpServiceMethod<ResponseT, ReturnT>)
          new SuspendForBody<>(
              requestFactory,
              callFactory,
              responseConverter,
              (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
              continuationBodyNullable);
  }

最終,對于本例子來說,parseAnnotations 方法最終的返回值是 SuspendForBody,它也是 HttpServiceMethod 的子類。其主要邏輯是:

  1. 將接口方法的最后一個參數強轉為 Continuation<ResponseT> 類型,這符合上述的分析
  2. 因為 isNullable 固定為 false,所以最終會調用 KotlinExtensions.await(call, continuation) 這個 Kotlin 的擴展函數
static final class SuspendForBody<ResponseT> extends HttpServiceMethod<ResponseT, Object> {
    private final CallAdapter<ResponseT, Call<ResponseT>> callAdapter;
    private final boolean isNullable;

    SuspendForBody(
        RequestFactory requestFactory,
        okhttp3.Call.Factory callFactory,
        Converter<ResponseBody, ResponseT> responseConverter,
        CallAdapter<ResponseT, Call<ResponseT>> callAdapter,
        boolean isNullable) {
      super(requestFactory, callFactory, responseConverter);
      this.callAdapter = callAdapter;
      this.isNullable = isNullable;
    }

    @Override
    protected Object adapt(Call<ResponseT> call, Object[] args) {
      call = callAdapter.adapt(call);
      //noinspection unchecked Checked by reflection inside RequestFactory.
      Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];
      try {
        return isNullable
            ? KotlinExtensions.awaitNullable(call, continuation)
            : KotlinExtensions.await(call, continuation);
      } catch (Exception e) {
        return KotlinExtensions.suspendAndThrow(e, continuation);
      }
    }
  }

await()方法就會以 suspendCancellableCoroutine 這個支持 cancel 的 CoroutineScope 作為作用域,依舊以 Call.enqueue的方式來發起 OkHttp 請求,拿到 responseBody 后就透傳出來,至此就完成了整個調用流程了

suspend fun <T : Any> Call<T>.await(): T {
  return suspendCancellableCoroutine { continuation ->
    continuation.invokeOnCancellation {
      cancel()
    }
    enqueue(object : Callback<T> {
      override fun onResponse(call: Call<T>, response: Response<T>) {
        if (response.isSuccessful) {
          val body = response.body()
          if (body == null) {
            val invocation = call.request().tag(Invocation::class.java)!!
            val method = invocation.method()
            val e = KotlinNullPointerException("Response from " +
                method.declaringClass.name +
                '.' +
                method.name +
                " was null but response body type was declared as non-null")
            continuation.resumeWithException(e)
          } else {
            continuation.resume(body)
          }
        } else {
          continuation.resumeWithException(HttpException(response))
        }
      }

      override fun onFailure(call: Call<T>, t: Throwable) {
        continuation.resumeWithException(t)
      }
    })
  }
}

十三、Retrofit & Android

上文有講到,Retrofit 的內部實現并不需要依賴于 Android 平臺,而是可以用于任意的 Java 客戶端,Retrofit 只是對 Android 平臺進行了特殊實現而已。那么,Retrofit 具體是對 Android 平臺做了什么特殊支持呢?

在構建 Retrofit 對象的時候,我們可以選擇傳遞一個 Platform 對象用于標記調用方所處的平臺

public static final class Builder {
    private final Platform platform;
    private @Nullable okhttp3.Call.Factory callFactory;
    private @Nullable HttpUrl baseUrl;
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
    private @Nullable Executor callbackExecutor;
    private boolean validateEagerly;

    Builder(Platform platform) {
      this.platform = platform;
    }

    public Builder() {
      this(Platform.get());
    }

    ···
}

Platform 類有兩個作用:

  1. 判斷是否支持 Java 8。這在判斷是否支持調用 interface 的默認方法,以及判斷是否支持 Optional 和 CompletableFuture 時需要用到。因為 Android 應用如果想要支持 Java 8 的話,是需要在 Gradle 文件中進行主動配置的,且 Java 8 在 Android 平臺上目前也支持得并不徹底,所以需要判斷是否支持 Java 8 來決定是否啟用特定功能
  2. 實現 main 線程回調的 Executor。眾所周知,Android 平臺是不允許在 main 線程上執行耗時任務的,且 UI 操作都需要切換到 main 線程來完成。所以,對于 Android 平臺來說,Retrofit 在回調網絡請求結果時,都會通過 main 線程執行的 Executor 來進行線程切換
class Platform {
  private static final Platform PLATFORM = findPlatform();

  static Platform get() {
    return PLATFORM;
  }

  private static Platform findPlatform() {
    //根據 JVM 名字來判斷使用方是否是 Android 平臺
    return "Dalvik".equals(System.getProperty("java.vm.name"))
        ? new Android() //
        : new Platform(true);
  }
  
  //是否支持 Java 8
  private final boolean hasJava8Types;
  private final @Nullable Constructor<Lookup> lookupConstructor;

  Platform(boolean hasJava8Types) {
    this.hasJava8Types = hasJava8Types;

    Constructor<Lookup> lookupConstructor = null;
    if (hasJava8Types) {
      try {
        // Because the service interface might not be public, we need to use a MethodHandle lookup
        // that ignores the visibility of the declaringClass.
        lookupConstructor = Lookup.class.getDeclaredConstructor(Class.class, int.class);
        lookupConstructor.setAccessible(true);
      } catch (NoClassDefFoundError ignored) {
        // Android API 24 or 25 where Lookup doesn't exist. Calling default methods on non-public
        // interfaces will fail, but there's nothing we can do about it.
      } catch (NoSuchMethodException ignored) {
        // Assume JDK 14+ which contains a fix that allows a regular lookup to succeed.
        // See https://bugs.openjdk.java.net/browse/JDK-8209005.
      }
    }
    this.lookupConstructor = lookupConstructor;
  }
    
  //獲取默認的 Executor 實現,用于 Android 平臺
  @Nullable
  Executor defaultCallbackExecutor() {
    return null;
  }

  List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
      @Nullable Executor callbackExecutor) {
    DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor);
    return hasJava8Types
        ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
        : singletonList(executorFactory);
  }

  int defaultCallAdapterFactoriesSize() {
    return hasJava8Types ? 2 : 1;
  }

  List<? extends Converter.Factory> defaultConverterFactories() {
    return hasJava8Types ? singletonList(OptionalConverterFactory.INSTANCE) : emptyList();
  }

  int defaultConverterFactoriesSize() {
    return hasJava8Types ? 1 : 0;
  }

  @IgnoreJRERequirement // Only called on API 24+.
  boolean isDefaultMethod(Method method) {
    return hasJava8Types && method.isDefault();
  }

  @IgnoreJRERequirement // Only called on API 26+.
  @Nullable
  Object invokeDefaultMethod(Method method, Class<?> declaringClass, Object object, Object... args)
      throws Throwable {
    Lookup lookup =
        lookupConstructor != null
            ? lookupConstructor.newInstance(declaringClass, -1 /* trusted */)
            : MethodHandles.lookup();
    return lookup.unreflectSpecial(method, declaringClass).bindTo(object).invokeWithArguments(args);
  }

}

Platform 類只具有一個唯一子類,即 Android 類。其主要邏輯就是重寫了父類的 defaultCallbackExecutor()方法,通過 Handler 來實現在 main 線程執行特定的 Runnable,以此來實現網絡請求結果都在 main 線程進行回調

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

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

    @Nullable
    @Override
    Object invokeDefaultMethod(
        Method method, Class<?> declaringClass, Object object, Object... args) throws Throwable {
      if (Build.VERSION.SDK_INT < 26) {
        throw new UnsupportedOperationException(
            "Calling default methods on API 24 and 25 is not supported");
      }
      return super.invokeDefaultMethod(method, declaringClass, object, args);
    }

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

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

前文也有講到,Retrofit 有個默認的 CallAdapter.Factory接口實現類,用于對接口方法返回值包裝類型是 Call 的情形進行處理。DefaultCallAdapterFactory 會拿到 Platform 返回的 Executor 對象,如果 Executor 對象不為 null 且接口方法沒有標注 SkipCallbackExecutor 注解的話,就使用該 Executor 對象作為一個代理來中轉所有的回調操作,以此實現線程切換。這里使用到了裝飾器模式

final class DefaultCallAdapterFactory extends CallAdapter.Factory {
  private final @Nullable Executor callbackExecutor;

  DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
    this.callbackExecutor = callbackExecutor;
  }

  @Override
  public @Nullable CallAdapter<?, ?> get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
      
    ···

    final Executor executor =
        //判斷 annotations 是否包含 SkipCallbackExecutor 注解
        //有的話說明希望直接在原來的線程進行方法調用,不需要進行線程切換
        Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
            ? null
            : callbackExecutor;

    return new CallAdapter<Object, Call<?>>() {
      @Override
      public Type responseType() {
        return responseType;
      }

      @Override
      public Call<Object> adapt(Call<Object> call) {
        //executor 不為 null 的話就將其作為一個中間代理
        //交由 ExecutorCallbackCall 來完成實際的回調操作
        return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
      }
    };
  }

  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) {
      Objects.requireNonNull(callback, "callback == null");

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

    ···
        
  }
}

十四、動態代理模式

在講retrofit.create這一節內容的時候有提到動態代理。動態代理模式是 Retrofit 能夠做到網絡請求如此簡潔方便的主要原因。有時候,對于某個既定的 interface,我們不希望直接聲明并使用其實現類,而是希望實現類可以動態生成,并且提供實現 AOP 編程的機會,此時就可以通過 Proxy.newProxyInstance來實現這個目的

/**
 * @Author: leavesCZY
 * @Github:https://github.com/leavesCZY
 */
data class UserBean(val userName: String, val userAge: Long)

interface ApiService {

    fun getUserData(id: String): UserBean

}

fun main() {
    val apiService = ApiService::class.java
    var newProxyInstance = Proxy.newProxyInstance(
        apiService.classLoader,
        arrayOf<Class<*>>(apiService), object : InvocationHandler {
            override fun invoke(proxy: Any, method: Method, args: Array<out Any>?): Any {
                println("methodName: " + method.name)
                args?.forEach {
                    println("args: " + it)
                }
                return UserBean("leavesC", 26)
            }

        })
    newProxyInstance = newProxyInstance as ApiService
    val userBean = newProxyInstance.getUserData("100")
    println("userBean: $userBean")
}

在上述例子中,雖然我們并沒有聲明ApiService的任何實現類,但是在 invoke方法中我們拿到了getUserData方法所代表的 method 對象以及請求參數 args,最終外部也獲得了返回值。這就是代理模式給我們帶來的便利之處

methodName: getUserData
args: 100
userBean: UserBean(userName=leavesC, userAge=26)

十五、結尾

Retrofit 的源碼就講到這里了,自我感覺還是講得挺全面的,雖然可能講得沒那么易于理解。從開始看源碼到寫完文章花了要十天出一些,斷斷續續地看源碼,斷斷續續地寫文章,總算寫完了。覺得對你有所幫助就請點個贊吧 ????

下篇文章就再來寫關于 Retrofit 的擴展知識吧 ~~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374