Android Retrofit原始碼解析:都能看懂的Retrofit使用詳解

南方吳彥祖_藍斯發表於2021-10-14

你在使用 Retrofit 的時候,是否會有如下幾點疑惑?

  • 什麼是動態代理?

  • 整個請求的流程是怎樣的?

  • 底層是如何用 OkHttp 請求的?

  • 方法上的註解是什麼時候解析的,怎麼解析的?

  • Converter 的轉換過程,怎麼透過 Gson 轉成對應的資料模型的?

  • CallAdapter 的替換過程,怎麼轉成 RxJava 進行操作的?

  • 如何支援 Kotlin 協程的 suspend 掛起函式的?

    關於 Kotlin 協程請求網路,首先寫一個 Demo 來看一下用協程是怎麼進行網路請求的,然後會再具體分析怎麼轉換成 Kotlin 的協程的請求

我會在文章中,透過原始碼,逐步解開疑惑,並且在最後文章結尾會再次總結,回答上面的幾個問題。

友情提示,本文略長但是沒有廢話,實打實的乾貨,學習需要耐心
Retrofit和 OkHttp是目前最廣泛使用的網路請求庫了,所以有必要了解它的原始碼,學習它的優秀的程式碼與設計,來提升自己。

本文的整體思路

首先先看一下 Retrofit 的基本用法,根據示例程式碼,作為分析原始碼的依據,以及分析原始碼的入口,來一步一步看一下 Retrofit 的工作機制。

本文的依賴

implementation 'com.squareup.okhttp3:okhttp:4.8.1'implementation 'com.squareup.retrofit2:retrofit:2.9.0'implementation 'com.squareup.retrofit2:converter-gson:2.5.0'implementation 'com.squareup.retrofit2:adapter-rxjava2:2.7.2'implementation 'com.google.code.gson:gson:2.8.6'implementation 'io.reactivex.rxjava3:rxjava:3.0.0'implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'複製程式碼

1.什麼是Retrofit

Retrofit:A type-safe  HTTP client for Android and Java。一個型別安全的 Http 請求的客戶端。

底層的網路請求是基於 OkHttp 的,Retrofit 對其做了封裝,提供了即方便又高效的網路訪問框架。

2.Retrofit的基本用法

class RetrofitActivity : AppCompatActivity() {    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)        //初始化一個Retrofit物件
        val retrofit = Retrofit.Builder()
            .baseUrl(")
            .addConverterFactory(GsonConverterFactory.create())
            .build()        //建立出GitHubApiService物件
        val service = retrofit.create(GitHubApiService::class.java)        //返回一個 Call 物件
        val repos = service.listRepos("octocat")        //呼叫 enqueue 方法在回撥方法裡處理結果
        repos.enqueue(object : Callback<List<Repo>?> {
            override fun onFailure(call: Call<List<Repo>?>, t: Throwable) {
                                t.printStackTrace()
            }
            override fun onResponse(call: Call<List<Repo>?>, response: Response<List<Repo>?>) {                "response.code() = ${response.code()}".logE()
            }
        })
    }
}
複製程式碼
//自己定義的 API 請求介面interface GitHubApiService {    @GET("users/{user}/repos")    fun listRepos(@Path("user") user: String?): Call<List<Repo>>
}
複製程式碼

以上就是 Retrofit 的基本用法。

沒什麼好講的,寫這個例子就是為了分析原始碼做準備,有一個原始碼分析的入口。

3.原始碼分析的準備工作

先看幾個表面上的類

  • Retrofit:總攬全域性一個類,一些配置,需要透過其內部  Builder 類構建,比如 CallAdapter、Converter 等
  • GitHubApiService:自己寫的 API 介面,透過 Retrofit 的  create 方法進行例項化
  • Call:Retrofit 的 Call,是執行網路請求的是一個頂層介面,需要看原始碼,具體實現類實際是一個  OkHttpCall,下面會具體說
  • Callback:請求結果回撥

接下來重點來了,進行原始碼分析。

4.原始碼分析

分析的入口是我們程式碼例子中的 repos.enqueue(object : Callback<List<Repo>?> {…})方法

點進去,看到是 Call 的 enqueue方法

public interface Call<T> extends Cloneable {  void enqueue(Callback<T> callback);
}
複製程式碼

這是一個介面,是我們 GitHubApiService 介面中定義的  listRepos 方法中返回的 Call 物件,現在就要看GitHubApiService 的初始化,以及具體返回的是  Call 物件是誰。

然後重點就要看  retrofit.create(GitHubApiService::class.java) 方法,來看下 GitHubApiService 具體是怎麼建立的,以及 Call 物件的實現類

5.Retrofit 的 create 方法

//Retrofit.javapublic <T> T create(final Class<T> service) {  //1
  validateServiceInterface(service);  //2
  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) {                return method.invoke(this, args);
              }
              args = args != null ? args : emptyArgs;              return platform.isDefaultMethod(method)
                  ? platform.invokeDefaultMethod(method, service, proxy, args)
                  : loadServiceMethod(method).invoke(args);
            }
          });
}
複製程式碼

註釋 1:這個方法,就是驗證我們定義的 GitHubApiService 是一個介面,且不是泛型介面,並且會判斷是否進行方法的 提前驗證,為了更好的把錯誤暴露的編譯期,這個不是我們的重點內容,具體程式碼就不看了。

註釋 2 :是一個動態代理的方法,來返回 GitHubApiService 的例項

動態代理?嗯?什麼是動態代理,接下來,我就寫一個具體的例子來展示一個動態代理的具體用法,以及什麼是動態代理?

先插播一段動態代理程式碼,這個是理解 Retrofit 的工作機制所必須的。

6.動態代理的示例

6.1.寫一個動態代理的 Demo

下面是一個  Java 專案,模擬一個 Retrofit 的請求過程

//模擬 Retrofit,定義 API 請求介面public interface GitHubApiService {    void listRepos(String user);
}
複製程式碼
public class ProxyDemo {    //程式的入口方法
    public static void main(String[] args) {        //透過動態代理獲取 ApiService 的物件
        GitHubApiService apiService = (GitHubApiService) Proxy.newProxyInstance(
                GitHubApiService.class.getClassLoader(),                new Class[]{GitHubApiService.class},                new InvocationHandler() {                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("method = " + method.getName() + "   args = " + Arrays.toString(args));                        return null;
                    }
                });
        System.out.println(apiService.getClass());        //呼叫 listRepos 方法
        apiService.listRepos("octcat");
    }
}
複製程式碼

執行  main 方法

當我們呼叫  apiService.listRepos("octcat");方法時,列印出來如下結果

class com.sun.proxy.$Proxy0method = listRepos   args = [octcat]
複製程式碼

可以看到當我們呼叫 listRepos方法的時候,InvocationHandler 的  invoke方法中攔截到了我們的方法,引數等資訊。 Retrofit 的原理其實就是這樣,攔截到方法、引數,再根據我們在方法上的註解,去拼接為一個正常的OkHttp 請求,然後執行。

日誌的第一行,在執行時這個類一個 $Proxy0的類。實際上,在執行期 GitHubApiService 的介面會動態的建立出 實現類也就是這個  $Proxy0類,它大概長下面這個樣子。

我做了一個點改動,方便檢視,本質上都是一樣的

class $Proxy0 extends Proxy implements GitHubApiService {    protected $Proxy0(InvocationHandler h) {        super(h);
    }    @Override
    public void listRepos(String user) {
        Method method = Class.forName("GitHubApiService").getMethod("listRepos", String.class);        super.h.invoke(this, method, new Object[]{user});
    }
}
複製程式碼

我們在呼叫 listRepos方法的時候,實際上呼叫的是 InvocationHandler 的  invoke 方法。

6.2總結

  • 在 ProxyDemo 程式碼執行中,會動態建立 GitHubApiService 介面的實現類,作為代理物件,執行InvocationHandler 的  invoke 方法。
  • 動態指的是在執行期,而代理指的是實現了GitHubApiService 介面的具體類,實現了介面的方法,稱之為代理
  • 本質上是在執行期,生成了 GitHubApiService 介面的實現類,呼叫了 InvocationHandler 的  invoke方法。

現在解決了第一個疑問: 什麼是動態代理

好的,動態代理已經知道是啥了,回到我們  retrofit.create(GitHubApiService::class.java)方法

7.再看 Retrofit 的 create 方法

//Retrofit.javapublic <T> T create(final Class<T> service) {
  validateServiceInterface(service);  return (T)
      Proxy.newProxyInstance(
          service.getClassLoader(),//1
          new Class<?>[] {service},//2
          new InvocationHandler() {//3
            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.
              //4
              if (method.getDeclaringClass() == Object.class) {                return method.invoke(this, args);
              }
              args = args != null ? args : emptyArgs;              //5
              return platform.isDefaultMethod(method)
                  ? platform.invokeDefaultMethod(method, service, proxy, args)
                  : loadServiceMethod(method).invoke(args);
            }
          });
}
複製程式碼

註釋 1:獲取一個 ClassLoader 物件

註釋 2:GitHubApiService 的位元組碼物件傳到陣列中去,也即是我們要代理的具體介面。

註釋 3:InvocationHandler 的  invoke 是關鍵,從上面動態代理的 Demo 中,我們知道,在GitHubApiService宣告的  listRepos方法在呼叫時,會執行 InvocationHandler 的 invoke的方法體。

註釋 4:因為有代理類的生成,預設繼承 Object 類,所以如果是 Object.class 走,預設呼叫它的方法

註釋 5:如果是預設方法(比如 Java8 ),就執行 platform 的預設方法。 否則執行 loadServiceMethod方法的 invoke方法

loadServiceMethod(method).invoke(args);這個方法是我們這個 Retrofit  最關鍵的程式碼,也是分析的重點入口

7.1.先看loadServiceMethod方法

我們先看 loadServiceMethod方法返回的是什麼物件,然後再看這個物件的  invoke 方法

//Retrofit.javaprivate final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
ServiceMethod<?> loadServiceMethod(Method method) {  //1
  ServiceMethod<?> result = serviceMethodCache.get(method);  if (result != null) return result;
  synchronized (serviceMethodCache) {
    result = serviceMethodCache.get(method);    if (result == null) {      //2
      result = ServiceMethod.parseAnnotations(this, method);      //3
      serviceMethodCache.put(method, result);
    }
  }  return result;
}
複製程式碼

註釋 1:從 ConcurrentHashMap 中取一個 ServiceMethod 如果存在直接返回

註釋 2:透過  ServiceMethod.parseAnnotations(this, method);方法建立一個 ServiceMethod 物件

註釋 3:用 Map 把建立的 ServiceMethod 物件快取起來,因為我們的請求方法可能會呼叫多次,快取提升效能。

看一下  ServiceMethod.parseAnnotations(this, method);方法具體返回的物件是什麼,然後再看它的  invoke 方法

7.2.ServiceMethod的parseAnnotations方法

這個方法接下來還會看,這裡我們只看現在需要的部分。

//ServiceMethod.javastatic <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
  ...  //1
  return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
複製程式碼

返回的是一個 HttpServiceMethod物件,那麼接下來看下它的 invoke 方法

7.3.HttpServiceMethod 的 invoke 方法

//HttpServiceMethod.java@Overridefinal @Nullable ReturnT invoke(Object[] args) {  //1
  Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);  //2
  return adapt(call, args);
}
複製程式碼

註釋 1:建立了一個Call物件,是 OkHttpCall,這個不就是在 GitHubApiService 這個介面宣告的 Call 物件嗎?

然後再看 OkHttpCall 的 enqueue方法,不就知道是怎麼進行請求,怎麼回撥的了嗎?

註釋 2:是一個 adapt 方法,在不使用 Kotlin 協程的情況下,其實呼叫的是子類 CallAdapted 的  adapt,這個會在下面具體分析,包括 Kotlin 協程的 suspend 函式

現在我們已經知道了 GitHubApiService 介面中定義的  listRepos中的 Call 物件,是 OkHttpCall,接下里看OkHttpCall 的  enqueue 方法

8.OkHttpCall的enqueue方法

這段程式碼比較長,但這個就是這個請求的關鍵,以及怎麼使用 OkHttp 進行請求的,如果解析 Response 的,如何回撥的。

//OkHttpCall.java@Overridepublic void enqueue(final Callback<T> callback) {
  Objects.requireNonNull(callback, "callback == null");  //1
  okhttp3.Call call;
  Throwable failure;  synchronized (this) {    if (executed) throw new IllegalStateException("Already executed.");
    executed = true;
    call = rawCall;
    failure = creationFailure;    if (call == null && failure == null) {      try {        //2
        call = rawCall = createRawCall();
      } catch (Throwable t) {
        throwIfFatal(t);
        failure = creationFailure = t;
      }
    }
  }  if (failure != null) {
    callback.onFailure(this, failure);    return;
  }  if (canceled) {
    call.cancel();
  }  //3
  call.enqueue(      new okhttp3.Callback() {        @Override
        public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
          Response<T> response;          try {            //4
            response = parseResponse(rawResponse);
          } catch (Throwable e) {
            throwIfFatal(e);
            callFailure(e);            return;
          }          try {            //5
            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 {            //6
            callback.onFailure(OkHttpCall.this, e);
          } catch (Throwable t) {
            throwIfFatal(t);
            t.printStackTrace(); // TODO this is not great
          }
        }
      });
}
複製程式碼

註釋 1:宣告一個 okhttp3.Call 物件,用來進行網路請求

註釋 2:給 okhttp3.Call 物件進行賦值,下面會具體看程式碼,如何建立了一個 okhttp3.Call 物件

註釋 3:呼叫 okhttp3.Call 的  enqueue 方法,進行真正的網路請求

註釋 4:解析響應,下面會具體看程式碼

註釋 5:成功的回撥

註釋 6:失敗的回撥

到現在,我們文章開頭兩個疑問得到解釋了

整個請求的流程是怎樣的?

底層是如何用 OkHttp 請求的?

我們還要看下一個 okhttp3.Call 物件是怎麼建立的,我們寫的註解引數是怎麼解析的,響應結果是如何解析的,也就是我們在 Retrofit 中配置  addConverterFactory(GsonConverterFactory.create())是如何直接拿到資料模型的。

8.1.okhttp3.Call 物件是怎麼建立的

看下  call = rawCall = createRawCall();方法

//OkHttpCall.javaprivate final okhttp3.Call.Factory callFactory;private okhttp3.Call createRawCall() throws IOException {  //1 callFactory是什麼
  okhttp3.Call call = callFactory.newCall(requestFactory.create(args));  if (call == null) {    throw new NullPointerException("Call.Factory returned null.");
  }  return call;
}
複製程式碼

透過 callFactory 建立的(callFactory應該是 OkHttpClient),看一下 callFactory 的賦值過程

//OkHttpCall.javaOkHttpCall(
    RequestFactory requestFactory,    Object[] args,
    okhttp3.Call.Factory callFactory,
    Converter<ResponseBody, T> responseConverter) {  this.requestFactory = requestFactory;  this.args = args;  //透過 OkHttpCall 構造直接賦值
  this.callFactory = callFactory;  this.responseConverter = responseConverter;
}
複製程式碼

在 OkHttpCall 構造中直接賦值,那接下來就繼續看 OkHttpCall 的初始化過程

//HttpServiceMethod.javaprivate final okhttp3.Call.Factory callFactory;@Overridefinal @Nullable ReturnT invoke(Object[] args) {  //在 OkHttpCall 例項化時賦值, callFactory 是 HttpServiceMethod 的成員變數
  Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);  return adapt(call, args);
}//callFactory 是在 HttpServiceMethod 的構造中賦值的HttpServiceMethod(
    RequestFactory requestFactory,
    okhttp3.Call.Factory callFactory,
    Converter<ResponseBody, ResponseT> responseConverter) {  this.requestFactory = requestFactory;    //透過 HttpServiceMethod 構造直接賦值
  this.callFactory = callFactory;  this.responseConverter = responseConverter;
}
複製程式碼

發現 callFactory 的值是在建立 HttpServiceMethod 時賦值的,繼續跟!

在 7.2 小節,有一行程式碼 HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);我們沒有跟進去,現在看一下  HttpServiceMethod 是怎麼建立的

//HttpServiceMethod.javastatic <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
    Retrofit retrofit, Method method, RequestFactory requestFactory) {  boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;  boolean continuationWantsResponse = false;  boolean continuationBodyNullable = false;    //1
  okhttp3.Call.Factory callFactory = retrofit.callFactory;  if (!isKotlinSuspendFunction) {    //2
    return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
  } else if (continuationWantsResponse) {    //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
    return (HttpServiceMethod<ResponseT, ReturnT>)        new SuspendForResponse<>(
            requestFactory,
            callFactory,
            responseConverter,
            (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
  } else {    //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
    return (HttpServiceMethod<ResponseT, ReturnT>)        new SuspendForBody<>(
            requestFactory,
            callFactory,
            responseConverter,
            (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
            continuationBodyNullable);
  }
}
複製程式碼

註釋 1:callFactory 的值是從 Retrofit 這個物件拿到的

註釋 2:如果不是 Kotlin 的掛起函式,返回是的 CallAdapted 物件

static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {}
複製程式碼

CallAdapted 是 HttpServiceMethod 的子類,會呼叫  adapt方法進行 CallAdapter 的轉換,我們後面會詳細看。

繼續看 Retrofit 的 callFactory 的值Retrofit是透過Builder構建的,看下Builder類

//Retrofit.javapublic static final class Builder {    public Retrofit build() {
      okhttp3.Call.Factory callFactory = this.callFactory;      if (callFactory == null) {        //1
        callFactory = new OkHttpClient();
      }      return new Retrofit(
          callFactory,
          baseUrl,
          unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories),
          callbackExecutor,
          validateEagerly);
    }
}
複製程式碼

原來 callFactory 實際是一個 OkHttpClient 物件,也就是 OkHttpClient 建立了一個 Call 物件,嗯就是 OKHttp 網路請求的那一套。

在建立okhttp3.Call 物件的  callFactory.newCall(requestFactory.create(args));方法中的  requestFactory.create(args)方法會返回一個 Request 的物件,這個我們也會在下面看是如何構造一個 OkHttp 的 Request 請求物件的。

8.2.請求註解引數是怎麼解析的

看  ServiceMethod.parseAnnotations(this, method);方法

//ServiceMethod.javastatic <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {  //1
  RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
    ...  //2
  return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
複製程式碼

註釋 1:透過 RequestFactory 解析註解,然後返回 RequestFactory 物件

註釋 2:把 RequestFactory 物件往 HttpServiceMethod 裡面傳遞,下面會具體看 RequestFactory 物件具體幹什麼用了?

繼續跟程式碼 RequestFactory.parseAnnotations

//RequestFactory.javafinal class RequestFactory {  static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {    //看build方法
    return new Builder(retrofit, method).build();
  }  //build方法
  RequestFactory build() {    //1
    for (Annotation annotation : methodAnnotations) {
      parseMethodAnnotation(annotation);
    }
   ....    return new RequestFactory(this);
  }
}
複製程式碼

遍歷 GitHubApiService 這個 API 介面上定義的方法註解,然後解析註解

繼續跟程式碼 parseMethodAnnotation

//RequestFactory.javaprivate void parseMethodAnnotation(Annotation annotation) {  if (annotation instanceof DELETE) {
    parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
  } else if (annotation instanceof GET) {
    parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
  }
  ...  else if (annotation instanceof POST) {
    parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
  }
    ....  else if (annotation instanceof Multipart) {    if (isFormEncoded) {      throw methodError(method, "Only one encoding annotation is allowed.");
    }
    isMultipart = true;
  } else if (annotation instanceof FormUrlEncoded) {    if (isMultipart) {      throw methodError(method, "Only one encoding annotation is allowed.");
    }
    isFormEncoded = true;
  }
}
複製程式碼

就是解析方法上的註解,來存到 RequestFactory 的內部。

其實 RequestFactory 這個類還有  parseParameter 和  parseParameterAnnotation這個就是解析方法引數宣告上的具體引數的註解,會在後面分析 Kotlin suspend 掛起函式具體講。

總之:具體程式碼就是分析方法上註解上面的值,方法引數上,這個就是細節問題了

總結就是:分析方法上的各個註解,方法引數上的註解,最後返回 RequestFactory 物件,給下面使用。

Retrofit 的大框架簡單,細節比較複雜。

RequestFactory 物件返回出去,具體幹嘛用了?大膽猜一下,解析出註解存到 RequestFactory 物件,這個物件身上可有各種請求的引數,然後肯定是類建立 OkHttp 的 Request請求物件啊,因為是用 OkHttp 請求的,它需要一個 Request 請求物件

8.3.RequestFactory 物件返回出去,具體幹嘛用了?

下面我就用一個程式碼塊貼了,看著更直接,我會具體表明屬於哪個類的

//ServiceMethod.javastatic <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {  //解析註解引數,獲取 RequestFactory 物件
  RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);  //把 RequestFactory 物件傳給 HttpServiceMethod
  return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}//注意換類了//HttpServiceMethod.javastatic <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
    Retrofit retrofit, Method method, RequestFactory requestFactory) {
    ...
  okhttp3.Call.Factory callFactory = retrofit.callFactory;  //不是 Kotlin 的掛起函式
  if (!isKotlinSuspendFunction) {    //把requestFactory傳給 CallAdapted
    return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
  } 
  ....
}//HttpServiceMethod.java//CallAdapted 是 HttpServiceMethod 的內部類也是 HttpServiceMethod 的子類CallAdapted(
    RequestFactory requestFactory,
    okhttp3.Call.Factory callFactory,
    Converter<ResponseBody, ResponseT> responseConverter,
    CallAdapter<ResponseT, ReturnT> callAdapter) {  //這裡把 requestFactory 傳給 super 父類的構造引數裡了,也就是 HttpServiceMethod
  super(requestFactory, callFactory, responseConverter);  this.callAdapter = callAdapter;
}//HttpServiceMethod.javaHttpServiceMethod(
    RequestFactory requestFactory,
    okhttp3.Call.Factory callFactory,
    Converter<ResponseBody, ResponseT> responseConverter) {  // HttpServiceMethod 的 requestFactory 成員變數儲存這個 RequestFactory 物件
  this.requestFactory = requestFactory;  this.callFactory = callFactory;  this.responseConverter = responseConverter;
}//因為會呼叫  HttpServiceMethod 的 invoke 方法//會把這個 RequestFactory 物件會繼續傳遞給 OkHttpCall 類中//注意換類了//OkHttpCall.javaOkHttpCall(
    RequestFactory requestFactory,
    Object[] args,
    okhttp3.Call.Factory callFactory,
    Converter<ResponseBody, T> responseConverter) {  //給 OkHttpCall 的requestFactory成員變數賦值
  this.requestFactory = requestFactory;  this.args = args;  this.callFactory = callFactory;  this.responseConverter = responseConverter;
}
複製程式碼

經過層層傳遞 RequestFactory 這個例項終於是到了 HttpServiceMethod 類中,最終傳到了 OkHttpCall 中,那這個 RequestFactory 物件在什麼時候使用呢? RequestFactory 會繼續在OkHttpCall中傳遞,因為  OkHttpCall 才是進行請求的。

在OkHttpCall的 建立 Call 物件時

//OkHttpCall.javaprivate okhttp3.Call createRawCall() throws IOException {  //1
  okhttp3.Call call = callFactory.newCall(requestFactory.create(args));  if (call == null) {    throw new NullPointerException("Call.Factory returned null.");
  }  return call;
}
複製程式碼

註釋 1:呼叫了 requestFactory.create(args)

注意:此時的RequestFactory的各個成員變數在解析註解那一步都賦值了

//RequestFactory.javaokhttp3.Request create(Object[] args) throws IOException {
  ...
  RequestBuilder requestBuilder =      new RequestBuilder(
          httpMethod,
          baseUrl,
          relativeUrl,
          headers,
          contentType,
          hasBody,
          isFormEncoded,
          isMultipart);
  ...  return requestBuilder.get().tag(Invocation.class, new Invocation(method, argumentList)).build();
}
複製程式碼

最終 requestFactory 的值用來構造 okhttp3.Request 的物件

以上就是解析註解,構造出okhttp3.Request的物件全過程了。

也就解答了 方法上的註解是什麼時候解析的,怎麼解析的?這個問題

8.4.請求響應結果是如何解析的

比如我們在構造 Retrofit 的時候加上  addConverterFactory(GsonConverterFactory.create())這行程式碼,我們的響應結果是如何透過 Gson 直接解析成資料模型的?

在 OkHttpCall 的 enqueue方法中

//OkHttpCall.java@Overridepublic 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 {            //1 解析響應
            response = parseResponse(rawResponse);
          } catch (Throwable e) {
            throwIfFatal(e);
            callFailure(e);            return;
          }
        }
    ...
      });
}
複製程式碼

註釋 1:透過 parseResponse解析響應返回給回撥介面

//OkHttpCall.javaprivate final Converter<ResponseBody, T> responseConverter;Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
  ResponseBody rawBody = rawResponse.body();
    ...
  ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);  try {    //1 透過 responseConverter 轉換 ResponseBody
    T body = responseConverter.convert(catchingBody);    return Response.success(body, rawResponse);
  } catch (RuntimeException e) {    // If the underlying source threw an exception, propagate that rather than indicating it was
    // a runtime exception.
    catchingBody.throwIfCaught();    throw e;
  }
}
複製程式碼

註釋 1:透過 responseConverter 呼叫 convert方法

首先那看 responseConverter 是什麼以及賦值的過程,然後再看 convert方法

//OkHttpCall.javaprivate final Converter<ResponseBody, T> responseConverter;
OkHttpCall(
    RequestFactory requestFactory,
    Object[] args,
    okhttp3.Call.Factory callFactory,
    Converter<ResponseBody, T> responseConverter) {  this.requestFactory = requestFactory;  this.args = args;  this.callFactory = callFactory;  //在構造中賦值
  this.responseConverter = responseConverter;
}// OkHttpCall 在 HttpServiceMethod 類中例項化//注意換類了//HttpServiceMethod.javaprivate final Converter<ResponseBody, ResponseT> responseConverter;
HttpServiceMethod(
    RequestFactory requestFactory,
    okhttp3.Call.Factory callFactory,
    Converter<ResponseBody, ResponseT> responseConverter) {  this.requestFactory = requestFactory;  this.callFactory = callFactory;   //在構造中賦值
  this.responseConverter = responseConverter;
}//HttpServiceMethod 在子類 CallAdapted 呼叫 super方法賦值CallAdapted(
    RequestFactory requestFactory,
    okhttp3.Call.Factory callFactory,
    Converter<ResponseBody, ResponseT> responseConverter,
    CallAdapter<ResponseT, ReturnT> callAdapter) {  //在CallAdapted中呼叫super賦值
  super(requestFactory, callFactory, responseConverter);  this.callAdapter = callAdapter;
}
複製程式碼

繼續看 CallAdapted 的初始化中  responseConverter 的賦值過程

//HttpServiceMethod.javastatic <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
    Retrofit retrofit, Method method, RequestFactory requestFactory) {
   ...
  CallAdapter<ResponseT, ReturnT> callAdapter =
      createCallAdapter(retrofit, method, adapterType, annotations);  //1 例項化responseConverter
  Converter<ResponseBody, ResponseT> responseConverter =
      createResponseConverter(retrofit, method, responseType);
  okhttp3.Call.Factory callFactory = retrofit.callFactory;  if (!isKotlinSuspendFunction) {    //2 CallAdapted的例項化賦值
    return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
  } 
  ...
}
複製程式碼

繼續跟程式碼  createResponseConverter方法

//HttpServiceMethod.javaprivate static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
    Retrofit retrofit, Method method, Type responseType) {
  Annotation[] annotations = method.getAnnotations();  try {    //呼叫的是 retrofit的方法
    return retrofit.responseBodyConverter(responseType, annotations);
  } catch (RuntimeException e) { // Wide exception range because factories are user code.
    throw methodError(method, e, "Unable to create converter for %s", responseType);
  }
}//注意換類了//Retrofit.javafinal List<Converter.Factory> converterFactories;public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {  //繼續跟 nextResponseBodyConverter
  return nextResponseBodyConverter(null, type, annotations);
}public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
    @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
    ...  //1 從 converterFactories工廠中遍歷取出
  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;
    }
  }
  ...
}
複製程式碼

註釋 1:從 converterFactories 遍歷取出一個來呼叫  responseBodyConverter 方法,注意根據 responseType  返回值型別來取到對應的 Converter,如果不為空,直接返回此 Converter 物件

看一下 converterFactories 這個物件的賦值過程

//Retrofit.javafinal List<Converter.Factory> converterFactories;
Retrofit(
    okhttp3.Call.Factory callFactory,
    HttpUrl baseUrl,
    List<Converter.Factory> converterFactories,
    List<CallAdapter.Factory> callAdapterFactories,    @Nullable Executor callbackExecutor,    boolean validateEagerly) {  this.callFactory = callFactory;  this.baseUrl = baseUrl;  this.converterFactories = converterFactories; // Copy+unmodifiable at call site.
  //透過 Retrofit 的構造賦值,Retrofit的 初始化是透過內部 Builder 類的build方法
  this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.
  this.callbackExecutor = callbackExecutor;  this.validateEagerly = validateEagerly;
}//Retrofit.java 內部類 Builder 類的build方法//Builder.java
 public Retrofit build() {
   ...      // Make a defensive copy of the converters.
     //1
      List<Converter.Factory> converterFactories =          new ArrayList<>(              1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());        //2
      converterFactories.add(new BuiltInConverters());        //3
      converterFactories.addAll(this.converterFactories);        //4
      converterFactories.addAll(platform.defaultConverterFactories());      return new Retrofit(
          callFactory,
          baseUrl,
          unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories),
          callbackExecutor,
          validateEagerly);
    }
複製程式碼

註釋 1:初始化 converterFactories 這個 list

註釋 2:新增預設的構建的轉換器,其實是 StreamingResponseBodyConverter 和 BufferingResponseBodyConverter

註釋 3:就是自己新增的轉換配置  addConverterFactory(GsonConverterFactory.create())

//Retrofit.java 內部類 Builder.javapublic Builder addConverterFactory(Converter.Factory factory) {
  converterFactories.add(Objects.requireNonNull(factory, "factory == null"));  return this;
}
複製程式碼

註釋 4:如果是 Java8 就是一個 OptionalConverterFactory 的轉換器否則就是一個空的

注意:是怎麼找到GsonConverterFactory來呼叫 Gson 的 convert方法的呢?在遍歷converterFactories時會根據  responseType來找到對應的轉換器。

具體 GsonConverterFactory 的  convert 方法就是 Gson 的邏輯了,就不是 Retrofit 的重點了。

到現在 Converter 的轉換過程,我們也就清楚了。

還有一個問題,我們寫的 API 介面是如何支援 RxJava 的

9.CallAdapter的替換過程

9.1.使用 RxJava 進行網路請求

怎麼轉成 RxJava

比如:我們在初始化一個Retrofit時加入  addCallAdapterFactory(RxJava2CallAdapterFactory.create())這行

//初始化一個Retrofit物件val retrofit = Retrofit.Builder()
    .baseUrl(")    //加入 RxJava2CallAdapterFactory 支援
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .addConverterFactory(GsonConverterFactory.create())
    .build()
複製程式碼

加入 RxJava2 的配置支援後,把 RxJava2CallAdapterFactory 存到 callAdapterFactories 這個集合中,記住這一點,下面要用到。

interface GitHubApiService {    @GET("users/{user}/repos")    fun listReposRx(@Path("user") user: String?): Single<Repo>
}
複製程式碼

我們就可以這麼請求介面了

//建立出GitHubApiService物件val service = retrofit.create(GitHubApiService::class.java)
service.listReposRx("octocat")
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ repo ->        "response name = ${repo[0].name}".logE()
    }, { error ->
        error.printStackTrace()
    })
複製程式碼

我們可以在自己定義的 API 介面中直接返回一個 RxJava 的 Single 物件的,來進行操作了。

我們下面就來看下是如何把請求物件轉換成一個 Single 物件的

//Retrofit.java 內部類 Builder.javapublic Builder addCallAdapterFactory(CallAdapter.Factory factory) {
  callAdapterFactories.add(Objects.requireNonNull(factory, "factory == null"));  return this;
}
複製程式碼

把 RxJava2CallAdapterFactory 存到了callAdapterFactories 這個 list 中了。

接下來我們看下是如何使用 callAdapterFactories 的 RxJava2CallAdapterFactory 中的這個 CallAdapter 的吧

這就要看我們之前看到了一個類了 HttpServiceMethod 的 parseAnnotations之前看過它的程式碼,只是上次看的是 Converter是如何賦值的也就是第 8.4 小節,這次看 CallAdapter 是如何被賦值使用的。

9.2CallAdapter是如何被賦值過程

HttpServiceMethod的 parseAnnotations方法

//HttpServiceMethod.javastatic <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
    Retrofit retrofit, Method method, RequestFactory requestFactory) {
  ....  //1
  CallAdapter<ResponseT, ReturnT> callAdapter =
      createCallAdapter(retrofit, method, adapterType, annotations);
  okhttp3.Call.Factory callFactory = retrofit.callFactory;  if (!isKotlinSuspendFunction) {    //2
    return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
  } 
    ...
}
複製程式碼

註釋 1:初始化 CallAdapter

註釋 2:給  CallAdapted 中的 callAdapter 變數賦值,然後呼叫它的 adapt 方法。

我們先找到具體 CallAdapter 賦值的物件,然後看它的 adapt就知道了,是如何轉換的了

接下來就是跟程式碼的過程了

//HttpServiceMethod.javaprivate static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
    Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {  try {    //noinspection unchecked
    //呼叫retrofit的callAdapter方法
    return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
  } catch (RuntimeException e) { // Wide exception range because factories are user code.
    throw methodError(method, e, "Unable to create call adapter for %s", returnType);
  }
}//Retrofit.javapublic CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {  //呼叫nextCallAdapter
  return nextCallAdapter(null, returnType, annotations);
}public CallAdapter<?, ?> nextCallAdapter(
    @Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {
  ...  //遍歷 callAdapterFactories
  int start = callAdapterFactories.indexOf(skipPast) + 1;  for (int i = start, count = callAdapterFactories.size(); i < count; i++) {    //是具體CallAdapterFactory的 get 方法
    CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);    if (adapter != null) {      return adapter;
    }
  }
  ...
}
複製程式碼

遍歷 callAdapterFactories 根據  returnType型別 來找到對應的 CallAdapter 返回

比如:我們在 GitHubApiService 的 returnType 型別為 Single,那麼返回的就是 RxJava2CallAdapterFactory 所獲取的 CallAdapter

interface GitHubApiService {    @GET("users/{user}/repos")    fun listReposRx(@Path("user") user: String?): Single<Repo>
}
複製程式碼

RxJava2CallAdapterFactory的  get方法

//RxJava2CallAdapterFactory.java@Override public @Nullable CallAdapter<?, ?> get(
    Type returnType, Annotation[] annotations, Retrofit retrofit) {
  Class<?> rawType = getRawType(returnType);  if (rawType == Completable.class) {    return new RxJava2CallAdapter(Void.class, scheduler, isAsync, false, true, false, false,        false, true);
  }  boolean isFlowable = rawType == Flowable.class;  //當前是Single型別
  boolean isSingle = rawType == Single.class;  boolean isMaybe = rawType == Maybe.class;  if (rawType != Observable.class && !isFlowable && !isSingle && !isMaybe) {    return null;
  }
  ...    //返回 RxJava2CallAdapter物件,isSingle引數為 true
  return new RxJava2CallAdapter(responseType, scheduler, isAsync, isResult, isBody, isFlowable,
      isSingle, isMaybe, false);
}
複製程式碼

返回的是 RxJava2CallAdapter 物件,並且根據 rawType 判斷當前是個什麼型別

看下 RxJava2CallAdapter 的 adapt方法

//RxJava2CallAdapter.java@Override public Object adapt(Call<R> call) {  //1 把Call包裝成一個Observable物件
  Observable<Response<R>> responseObservable = isAsync
      ? new CallEnqueueObservable<>(call)
      : new CallExecuteObservable<>(call);
  Observable<?> observable;  if (isResult) {
    observable = new ResultObservable<>(responseObservable);
  } else if (isBody) {
    observable = new BodyObservable<>(responseObservable);
  } else {
    observable = responseObservable;
  }  if (scheduler != null) {
    observable = observable.subscribeOn(scheduler);
  }  if (isFlowable) {    return observable.toFlowable(BackpressureStrategy.LATEST);
  }  //2
  if (isSingle) {    return observable.singleOrError();
  }  if (isMaybe) {    return observable.singleElement();
  }  if (isCompletable) {    return observable.ignoreElements();
  }  return RxJavaPlugins.onAssembly(observable);
}
複製程式碼

註釋 1:把 Call 包裝成一個 Observable 物件

註釋2:如果是 Single 則呼叫 observable.singleOrError();方法

到目前為止, CallAdapter 怎麼變成一個 RxJava2CallAdapter 以及它的具體呼叫,我們也就清楚了。

10.Retrofit 如何支援 Kotlin 協程的 suspend 掛起函式的?

整個流程中還有一點我們沒有分析 Retrofit  如何支援 Kotlin 協程的 suspend 掛起函式的?

首先寫一個 Demo 來看一下協程是怎麼進行網路請求的

10.1.Kotlin 協程請求網路的 Demo

新增依賴

def kotlin_coroutines = '1.3.7'implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines"implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines"implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'複製程式碼

定義請求介面,寫一個掛起函式

interface GitHubApiService {    //使用 Kotlin 協程 ,定義一個掛起函式
    @GET("users/{user}/repos")    suspend fun listReposKt(@Path("user") user: String?): List<Repo>
}
複製程式碼

請求介面

//建立出GitHubApiService物件val service = retrofit.create(GitHubApiService::class.java)//lifecycle提供的協程的Scope,因為 suspend 函式需要執行在協程裡面lifecycleScope.launchWhenResumed {    try {
        val repo = service.listReposKt("octocat")        "response name = ${repo[0].name}".logE()
    } catch (e: Exception) {
        e.printStackTrace()        //出錯邏輯
        //ignore
    }
}
複製程式碼

以上就是一個,用 Kotlin 協程進行網路請求的,Retrofit 是支援 Kotlin 協程的,接下來看下,Retrofit 是怎麼支援的。

10.2.分析Kotlin 協程的掛起函式的準備工作

首先在開始之前,我們得先得從程式碼角度知道,Kotlin 的 suspend 函式對應的 Java 類是什麼樣子,不然,就一個  suspend 關鍵字根本就沒法進行分析。

我寫一個 suspend 的測試方法,然後轉換成 java 方法看一下,這個 suspend 函式是個啥。

寫一個 Top Level 的Suspend.kt檔案(在文章最後我會給出原始碼,一看就明白)

在檔案中寫了一個測試的 suspend 函式

suspend fun test(name: String) {
}
複製程式碼

我們透過 Android Studio 再帶的工具,如下圖:把 Kotlin 方法轉成 Java 方法

Android Retrofit原始碼解析:都能看懂的Retrofit使用詳解

點這個按鈕

Android Retrofit原始碼解析:都能看懂的Retrofit使用詳解


結果如下

public final class SuspendKt {   @Nullable
   public static final Object test(@NotNull String name, @NotNull Continuation $completion) {      return Unit.INSTANCE;
   }
}
複製程式碼

看到了,我們的 suspend 的關鍵字,變成了  test 方法的一個Continuation引數,且為最後一個引數

看一下這個 Continuation類記住這個類,下面在分析的時候會遇到

@SinceKotlin("1.3")public interface Continuation<in T> {    public val context: CoroutineContext    public fun resumeWith(result: Result<T>)}
複製程式碼

好目前的準備工作都已經完成,開始分析  Retrofit 是怎麼支援 Kotlin 協程的掛起函式的。

10.3.Retrofit 是怎麼支援 Kotlin 協程的掛起函式的。

經過前面的原始碼解讀,我們知道,最終會呼叫到 HttpServiceMethod 的  parseAnnotations 方法

10.3.1.我們再看下這個方法,這次只看有關 協程的部分
//HttpServiceMethod.javastatic <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
    Retrofit retrofit, Method method, RequestFactory requestFactory) {    //1 獲取 isKotlinSuspendFunction 的值,這個會在下面具體分析
  boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;  boolean continuationWantsResponse = false;  boolean continuationBodyNullable = false;
  Annotation[] annotations = method.getAnnotations();
  Type adapterType;  //2 如果是 Kotlin 掛起函式
  if (isKotlinSuspendFunction) {
    Type[] parameterTypes = method.getGenericParameterTypes();
    Type responseType =
        Utils.getParameterLowerBound(            0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);    if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
      responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);      //3 continuationWantsResponse 賦值為 true
      continuationWantsResponse = true;
    } else {
    }
    adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
    annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
  } else {
    adapterType = method.getGenericReturnType();
  }
  Converter<ResponseBody, ResponseT> responseConverter =
      createResponseConverter(retrofit, method, responseType);
  okhttp3.Call.Factory callFactory = retrofit.callFactory;  if (!isKotlinSuspendFunction) {    return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
  } else if (continuationWantsResponse) {    //4 返回 SuspendForResponse 它是 HttpServiceMethod的子類
    return (HttpServiceMethod<ResponseT, ReturnT>)        new SuspendForResponse<>(
            requestFactory,
            callFactory,
            responseConverter,
            (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
  } else {    return (HttpServiceMethod<ResponseT, ReturnT>)        new SuspendForBody<>(
            requestFactory,
            callFactory,
            responseConverter,
            (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
            continuationBodyNullable);
  }
}
複製程式碼

註釋 1:獲取 isKotlinSuspendFunction 的值,這個會在下面具體分析

註釋 2:如果是 Kotlin 掛起函式,進入此程式碼塊

註釋 3:把 continuationWantsResponse 賦值為 true

註釋 4:返回 SuspendForResponse 它是 HttpServiceMethod 的子類,然後看它的  adapt方法,這個會在下面具體分析

獲取 isKotlinSuspendFunction 的值的過程

10.3.2.看 requestFactory 的 isKotlinSuspendFunction 賦值

requestFactory 這個類,我們之前分析過,就是解析註解的,但是有一部分沒看,就是解析方法引數上的註解,這次就看下。

//RequestFactory.javaprivate @Nullable ParameterHandler<?> parseParameter(    int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
  ParameterHandler<?> result = null;  if (annotations != null) {    for (Annotation annotation : annotations) {
      ParameterHandler<?> annotationAction =        //1 遍歷解析引數的註解,就是 @Path @Query @Field 等註解,具體就不看了,不是協程的重點
          parseParameterAnnotation(p, parameterType, annotations, annotation);      if (annotationAction == null) {        continue;
      }      if (result != null) {        throw parameterError(
            method, p, "Multiple Retrofit annotations found, only one allowed.");
      }
      result = annotationAction;
    }
  }  if (result == null) {    //2 如果是協程 ,其實比的就是最後一個值
    if (allowContinuation) {      try {        //3 判斷引數型別是 Continuation,這個介面,前面在 10.2 小節寫 Demo 時提過
        if (Utils.getRawType(parameterType) == Continuation.class) {          // 4 isKotlinSuspendFunction 賦值為 true
          isKotlinSuspendFunction = true;          return null;
        }
      } catch (NoClassDefFoundError ignored) {
      }
    }    throw parameterError(method, p, "No Retrofit annotation found.");
  }  return result;
}
複製程式碼

註釋 1:遍歷解析引數的註解,就是 @Path @Query @Field 等註解,具體就不看了,不是協程的重點

註釋 2:如果是協程 ,其實比的就是最後一個值

註釋 3:判斷引數型別是 Continuation,這個介面,前面在 10.2 小節寫 Demo 時提過

註釋 4:isKotlinSuspendFunction 賦值為 true

如果isKotlinSuspendFunction 為 true 時,返回就是 SuspendForResponse 類

接下來就要 SuspendForResponse 以及它的  adapt 方法了

10.3.3.看一下SuspendForResponse類
static final class SuspendForResponse<ResponseT> extends HttpServiceMethod<ResponseT, Object> {  private final CallAdapter<ResponseT, Call<ResponseT>> callAdapter;
  SuspendForResponse(
      RequestFactory requestFactory,
      okhttp3.Call.Factory callFactory,
      Converter<ResponseBody, ResponseT> responseConverter,
      CallAdapter<ResponseT, Call<ResponseT>> callAdapter) {    super(requestFactory, callFactory, responseConverter);    this.callAdapter = callAdapter;
  }  @Override
  protected Object adapt(Call<ResponseT> call, Object[] args) {    //1
    call = callAdapter.adapt(call);    //noinspection unchecked Checked by reflection inside RequestFactory.
    //2
    Continuation<Response<ResponseT>> continuation =
        (Continuation<Response<ResponseT>>) args[args.length - 1];    // See SuspendForBody for explanation about this try/catch.
    try {      //3
      return KotlinExtensions.awaitResponse(call, continuation);
    } catch (Exception e) {      //4
      return KotlinExtensions.suspendAndThrow(e, continuation);
    }
  }
}
複製程式碼

註釋 1:呼叫 callAdapter 代理 call 方法

註釋 2:取出最後一個引數,強轉成 Continuation 型別,想想我們寫的 Demo

註釋 3:Call 的擴充套件函式(Kotlin 的寫法)下面具體看下  awaitResponse

註釋 4:出現異常,丟擲異常。所以我們要在程式碼中,要主動 try catch,來處理錯誤

10.3.4.看一下Call的擴充套件函式
//KotlinExtensions.ktsuspend fun <T> Call<T>.awaitResponse(): Response<T> {  return suspendCancellableCoroutine { continuation ->
    continuation.invokeOnCancellation {
      cancel()
    }        //呼叫 Call的enqueue方法
    enqueue(object : Callback<T> {      override fun onResponse(call: Call<T>, response: Response<T>) {        //成功回撥
        continuation.resume(response)
      }      override fun onFailure(call: Call<T>, t: Throwable) {        //失敗回撥
        continuation.resumeWithException(t)
      }
    })
  }
}
複製程式碼

到現在,整個用 Kotlin 協程的請求過程我們也就看完了。

11.總結

至此,整個 Retrofit 的整體流程就分析完了,具體細節還需要好好研究,我們再總結一下,回答開始的問題

11.1.什麼是動態代理?

分兩點 動態指的是在執行期,而代理指的是實現了某個介面的具體類,稱之為代理,會呼叫了 InvocationHandler 的  invoke方法。

Retrofit 中的動態代理:

  • 在程式碼執行中,會動態建立 GitHubApiService 介面的實現類,作為代理物件,代理介面的方法
  • 在我們呼叫GitHubApiService 介面的實現類的  listRepos方法時,會呼叫了 InvocationHandler 的  invoke方法。
  • 本質上是在執行期,生成了 GitHubApiService 介面的實現類,呼叫了 InvocationHandler 的  invoke方法。

具體看第 6 節

11.2.整個請求的流程是怎樣的

  • 我們在呼叫 GitHubApiService 介面的  listRepos方法時,會呼叫 InvocationHandler 的  invoke方法
  • 然後執行  loadServiceMethod方法並返回一個 HttpServiceMethod 物件並呼叫它的  invoke方法
  • 然後執行 OkHttpCall的  enqueue方法
  • 本質執行的是 okhttp3.Call 的  enqueue方法
  • 當然這期間會解析方法上的註解,方法的引數註解,拼成 okhttp3.Call 需要的 okhttp3.Request 物件
  • 然後透過 Converter 來解析返回的響應資料,並回撥 CallBack 介面

以上就是這個Retrofit 的請求流程

11.3.底層是如何用 OkHttp 請求的?

看下第 11.2小節的解釋吧

具體看第 8 節

11.4.方法上的註解是什麼時候解析的,怎麼解析的?

  • 在 ServiceMethod.parseAnnotations(this, method); 方法中開始的
  • 具體內容是在 RequestFactory 類中,進行解析註解的
  • 呼叫 RequestFactory.parseAnnotations(retrofit, method); 方法實現的

具體看第 8.2 小節

11.5.Converter 的轉換過程,怎麼透過 Gson 轉成對應的資料模型的?

  • 透過成功回撥的  parseResponse(rawResponse);方法開始
  • 透過 responseConverter 的  convert 方法
  • responseConverter 是透過 converterFactories 透過遍歷,根據返回值型別來使用對應的 Converter 解析

具體看第 8.4 小節

11.6.CallAdapter 的替換過程,怎麼轉成 RxJava 進行操作的?

  • 透過配置 addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 在 callAdapterFactories 這個 list 中新增 RxJava2CallAdapterFactory
  • 如果不是 Kotlin 掛起函式最終呼叫的是 CallAdapted 的  adapt方法
  • callAdapter 的例項是透過 callAdapterFactories 這個 list 透過遍歷,根據返回值型別來選擇合適的CallAdapter

具體看第 9 節

11.7.如何支援 Kotlin 協程的 suspend 掛起函式的?

  • 透過 RequestFactory 解析方法上的引數值來判斷是不是一個掛起函式,並把 isKotlinSuspendFunction 變數置為 true
  • 根據 isKotlinSuspendFunction 這個變數來判斷響應型別是否是 Response 型別,然後把continuationWantsResponse 置為 true
  • 根據 continuationWantsResponse 這個變數,來返回 SuspendForResponse 物件
  • 並呼叫 SuspendForResponse 的 invoke 方法
  • 透過 Call 的擴充套件函式,來呼叫 Call 的 enqueue方法
  • 透過協程來返回

具體看第 10 節

到此為止,這篇文章算寫完了,當然還有很多具體細節沒有研究,但對 Retrofit 的各個方面都進行了閱讀。
Android Retrofit原始碼解析 ——→ 影片地址

作者:AboBack
連結:
來源:稀土掘金
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69983917/viewspace-2803041/,如需轉載,請註明出處,否則將追究法律責任。

相關文章