三方庫源碼筆記(7)-Retrofit 源碼詳解

公衆號:字節數組,但願對你有所幫助 🤣🤣java

對於 Android Developer 來講,不少開源庫都是屬於開發必備的知識點,從使用方式到實現原理再到源碼解析,這些都須要咱們有必定程度的瞭解和運用能力。因此我打算來寫一系列關於開源庫源碼解析實戰演練的文章,初定的目標是 EventBus、ARouter、LeakCanary、Retrofit、Glide、OkHttp、Coil 等七個知名開源庫,但願對你有所幫助 🤣🤣git

系列文章導航:github

1、前言

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

Retrofit 是這麼自我介紹的:A type-safe HTTP client for Android and Java. 這說明 Retrofit 的內部實現並不須要依賴於 Android 平臺,而是能夠用於任意的 Java 客戶端,Retrofit 只是對 Android 平臺進行了特殊實現而已。此外,如今 Android 平臺的主流開發語言早已經是 Kotlin 了,因此本篇文章所寫的例子都採用了 Kotlin ~json

對 Kotlin 語言不熟悉的同窗能夠看個人這篇文章來入門:兩萬六千字帶你 Kotlin 入門設計模式

Retrofit 的源碼並不算太複雜,但因爲應用了不少種設計模式,因此在流程上會比較繞。筆者從 2020/10/10 開始看源碼,陸陸續續看了幾天源碼後就開始動筆,但總感受無法闡述得特別清晰,寫着寫着就成了目前的樣子。讀者若是以爲我有哪裏寫得不太好的地方也歡迎給下建議 😂😂api

2、小例子

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

先引入當前 Retrofit 的最新版本:緩存

dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
}
複製代碼

在除了 Retrofit 外不引入其它任何依賴庫的狀況下,咱們發起一個網絡請求的流程大體以下所示:markdown

/** * 做者:leavesC * 時間:2020/10/13 0:05 * 描述: * GitHub:https://github.com/leavesC */
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,以便將請求結果轉交給外部

一、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 對象了

/** * 做者:leavesC * 時間:2020/10/13 0:05 * 描述: * GitHub:https://github.com/leavesC */
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")
        }
    })
}
複製代碼

二、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 的時候就會自動發起網絡請求

/** * 做者:leavesC * 時間:2020/10/13 0:05 * 描述: * GitHub:https://github.com/leavesC */
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")
        }
    })
}
複製代碼

三、提出疑問

能夠看到,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 的呢?

3、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;
  }
複製代碼

4、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);
    
}
複製代碼

5、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);
    }
  }
複製代碼

6、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
            }
          }
        });
  }
 
   ···
    
}
複製代碼

7、作個總結

以上幾個小節的內容講了在發起以下請求的過程當中涉及到的全部流程,但單純這樣看的話其實有點難把握各個小點,我本身看着都有點繞,因此這裏就再來回顧下以上內容,把全部知識點給串聯起來

  • 首先,咱們經過 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 給隱藏在了內部

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

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;
  	}
     
}
複製代碼

9、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());
  }
複製代碼

10、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());
  }
複製代碼

11、再總結下

這裏再來總結下上面兩節關於 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));
        }
      }
    }

    ···
  }
}

複製代碼

12、使用 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 線程就中止了。須要注意的是,在實際開發中應該避免這樣來使用協程,不然使用協程就沒有多少意義了

/** * 做者:leavesC * 時間:2020/10/19 22:00 * 描述: * GitHub:https://github.com/leavesC */
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)
      }
    })
  }
}
複製代碼

十3、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));
            }
          });
    }

    ···
        
  }
}
複製代碼

十4、動態代理模式

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

/** * 做者:leavesC * 時間:2020/10/20 22:53 * 描述: * GitHub:https://github.com/leavesC */
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)
複製代碼

十5、結尾

Retrofit 的源碼就講到這裏了,自我感受仍是講得挺全面的,雖然可能講得沒那麼易於理解 =_= 從開始看源碼到寫完文章花了要十天出一些,斷斷續續地看源碼,斷斷續續地寫文章,總算寫完了。以爲對你有所幫助就請點個贊吧 😂😂

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

相關文章
相關標籤/搜索