一定能看懂的 Retrofit 最詳細(xì)的源碼解析!

你在使用 Retrofit 的時候揉忘,是否會有如下幾點(diǎn)疑惑跳座?

  • 什么是動態(tài)代理?

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

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

  • 方法上的注解是什么時候解析的,怎么解析的乳蓄?

  • Converter 的轉(zhuǎn)換過程咪橙,怎么通過 Gson 轉(zhuǎn)成對應(yīng)的數(shù)據(jù)模型的?

  • CallAdapter 的替換過程虚倒,怎么轉(zhuǎn)成 RxJava 進(jìn)行操作的?

  • 如何支持 Kotlin 協(xié)程的 suspend 掛起函數(shù)的产舞?

    關(guān)于 Kotlin 協(xié)程請求網(wǎng)絡(luò)魂奥,首先寫一個 Demo 來看一下用協(xié)程是怎么進(jìn)行網(wǎng)絡(luò)請求的,然后會再具體分析怎么轉(zhuǎn)換成 Kotlin 的協(xié)程的請求

我會在文章中易猫,通過源碼耻煤,逐步解開疑惑,并且在最后文章結(jié)尾會再次總結(jié),回答上面的幾個問題哈蝇。

友情提示棺妓,本文略長但是沒有廢話,實(shí)打?qū)嵉母韶浥谏猓瑢W(xué)習(xí)需要耐心

RetrofitOkHttp 是目前最廣泛使用的網(wǎng)絡(luò)請求庫了怜跑,所以有必要了解它的源碼,學(xué)習(xí)它的優(yōu)秀的代碼與設(shè)計吠勘,來提升自己性芬。

本文的整體思路

首先先看一下 Retrofit 的基本用法,根據(jù)示例代碼剧防,作為分析源碼的依據(jù)植锉,以及分析源碼的入口,來一步一步看一下 Retrofit 的工作機(jī)制峭拘。

本文的依賴

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'
復(fù)制代碼

1.什么是Retrofit

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

底層的網(wǎng)絡(luò)請求是基于 OkHttp 的鸡挠,Retrofit 對其做了封裝暇赤,提供了即方便又高效的網(wǎng)絡(luò)訪問框架。

2.Retrofit的基本用法

class RetrofitActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //初始化一個Retrofit對象
        val retrofit = Retrofit.Builder()
            .baseUrl("https://api.github.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
        //創(chuàng)建出GitHubApiService對象
        val service = retrofit.create(GitHubApiService::class.java)
        //返回一個 Call 對象
        val repos = service.listRepos("octocat")
        //調(diào)用 enqueue 方法在回調(diào)方法里處理結(jié)果
        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()
            }
        })

    }
}
復(fù)制代碼
//自己定義的 API 請求接口
interface GitHubApiService {
    @GET("users/{user}/repos")
    fun listRepos(@Path("user") user: String?): Call<List<Repo>>
}
復(fù)制代碼

以上就是 Retrofit 的基本用法宵凌。

沒什么好講的鞋囊,寫這個例子就是為了分析源碼做準(zhǔn)備,有一個源碼分析的入口瞎惫。

3.源碼分析的準(zhǔn)備工作

先看幾個表面上的類

  • Retrofit:總攬全局一個類溜腐,一些配置,需要通過其內(nèi)部 Builder 類構(gòu)建瓜喇,比如 CallAdapter挺益、Converter 等
  • GitHubApiService:自己寫的 API 接口,通過 Retrofit 的 create 方法進(jìn)行實(shí)例化
  • Call:Retrofit 的 Call乘寒,是執(zhí)行網(wǎng)絡(luò)請求的是一個頂層接口望众,需要看源碼,具體實(shí)現(xiàn)類實(shí)際是一個 OkHttpCall伞辛,下面會具體說
  • Callback:請求結(jié)果回調(diào)

接下來重點(diǎn)來了烂翰,進(jìn)行源碼分析。

4.源碼分析

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

點(diǎn)進(jìn)去蚤氏,看到是 Call 的enqueue方法

public interface Call<T> extends Cloneable {

  void enqueue(Callback<T> callback);

}
復(fù)制代碼

這是一個接口甘耿,是我們 GitHubApiService 接口中定義的 listRepos 方法中返回的 Call 對象,現(xiàn)在就要看GitHubApiService 的初始化竿滨,以及具體返回的是 Call 對象是誰佳恬。

然后重點(diǎn)就要看 retrofit.create(GitHubApiService::class.java) 方法捏境,來看下 GitHubApiService 具體是怎么創(chuàng)建的仿贬,以及 Call 對象的實(shí)現(xiàn)類

5.Retrofit 的 create 方法

//Retrofit.java
public <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);
            }
          });
}
復(fù)制代碼

注釋 1:這個方法超歌,就是驗(yàn)證我們定義的 GitHubApiService 是一個接口,且不是泛型接口瞻讽,并且會判斷是否進(jìn)行方法的提前驗(yàn)證倾剿,為了更好的把錯誤暴露的編譯期筷频,這個不是我們的重點(diǎn)內(nèi)容,具體代碼就不看了柱告。

注釋 2 :是一個動態(tài)代理的方法截驮,來返回 GitHubApiService 的實(shí)例

動態(tài)代理?嗯际度?什么是動態(tài)代理葵袭,接下來,我就寫一個具體的例子來展示一個動態(tài)代理的具體用法乖菱,以及什么是動態(tài)代理坡锡?

先插播一段動態(tài)代理代碼,這個是理解 Retrofit 的工作機(jī)制所必須的窒所。

6.動態(tài)代理的示例

6.1.寫一個動態(tài)代理的 Demo

下面是一個 Java 項(xiàng)目鹉勒,模擬一個 Retrofit 的請求過程

//模擬 Retrofit,定義 API 請求接口
public interface GitHubApiService {
    void listRepos(String user);
}
復(fù)制代碼
public class ProxyDemo {
    //程序的入口方法
    public static void main(String[] args) {
        //通過動態(tài)代理獲取 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());
        //調(diào)用 listRepos 方法
        apiService.listRepos("octcat");
    }

}
復(fù)制代碼

執(zhí)行 main 方法

當(dāng)我們調(diào)用 apiService.listRepos("octcat");方法時,打印出來如下結(jié)果

class com.sun.proxy.$Proxy0
method = listRepos   args = [octcat]
復(fù)制代碼

可以看到當(dāng)我們調(diào)用listRepos方法的時候吵取,InvocationHandler 的 invoke方法中攔截到了我們的方法禽额,參數(shù)等信息。Retrofit 的原理其實(shí)就是這樣皮官,攔截到方法脯倒、參數(shù),再根據(jù)我們在方法上的注解捺氢,去拼接為一個正常的OkHttp 請求藻丢,然后執(zhí)行。

日志的第一行摄乒,在運(yùn)行時這個類一個$Proxy0的類悠反。實(shí)際上,在運(yùn)行期 GitHubApiService 的接口會動態(tài)的創(chuàng)建出實(shí)現(xiàn)類也就是這個 $Proxy0類馍佑,它大概長下面這個樣子斋否,具體的看鴻洋這篇文章 從一道面試題開始說起 枚舉、動態(tài)代理的原理

我做了一個點(diǎn)改動挤茄,方便查看如叼,本質(zhì)上都是一樣的

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});
    }
}
復(fù)制代碼

我們在調(diào)用listRepos方法的時候,實(shí)際上調(diào)用的是 InvocationHandler 的 invoke 方法穷劈。

6.2總結(jié)

  • 在 ProxyDemo 代碼運(yùn)行中笼恰,會動態(tài)創(chuàng)建 GitHubApiService 接口的實(shí)現(xiàn)類,作為代理對象歇终,執(zhí)行InvocationHandler 的 invoke 方法社证。
  • 動態(tài)指的是在運(yùn)行期,而代理指的是實(shí)現(xiàn)了GitHubApiService 接口的具體類评凝,實(shí)現(xiàn)了接口的方法追葡,稱之為代理
  • 本質(zhì)上是在運(yùn)行期,生成了 GitHubApiService 接口的實(shí)現(xiàn)類奕短,調(diào)用了 InvocationHandler 的 invoke方法宜肉。

現(xiàn)在解決了第一個疑問:什么是動態(tài)代理

好的,動態(tài)代理已經(jīng)知道是啥了翎碑,回到我們 retrofit.create(GitHubApiService::class.java)方法

7.再看 Retrofit 的 create 方法

//Retrofit.java
public <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);
            }
          });
}
復(fù)制代碼

注釋 1:獲取一個 ClassLoader 對象

注釋 2:GitHubApiService 的字節(jié)碼對象傳到數(shù)組中去谬返,也即是我們要代理的具體接口。

注釋 3:InvocationHandler 的 invoke 是關(guān)鍵日杈,從上面動態(tài)代理的 Demo 中遣铝,我們知道,在GitHubApiService聲明的 listRepos方法在調(diào)用時莉擒,會執(zhí)行 InvocationHandler 的invoke的方法體酿炸。

注釋 4:因?yàn)橛写眍惖纳桑J(rèn)繼承 Object 類涨冀,所以如果是 Object.class 走填硕,默認(rèn)調(diào)用它的方法

注釋 5:如果是默認(rèn)方法(比如 Java8 ),就執(zhí)行 platform 的默認(rèn)方法鹿鳖。否則執(zhí)行loadServiceMethod方法的invoke方法

loadServiceMethod(method).invoke(args);這個方法是我們這個 Retrofit 最關(guān)鍵的代碼扁眯,也是分析的重點(diǎn)入口

7.1.先看loadServiceMethod方法

我們先看loadServiceMethod方法返回的是什么對象,然后再看這個對象的 invoke 方法

//Retrofit.java
private 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;
}
復(fù)制代碼

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

注釋 2:通過 ServiceMethod.parseAnnotations(this, method);方法創(chuàng)建一個 ServiceMethod 對象

注釋 3:用 Map 把創(chuàng)建的 ServiceMethod 對象緩存起來栓辜,因?yàn)槲覀兊恼埱蠓椒赡軙{(diào)用多次恋拍,緩存提升性能。

看一下 ServiceMethod.parseAnnotations(this, method);方法具體返回的對象是什么藕甩,然后再看它的 invoke 方法

7.2.ServiceMethod的parseAnnotations方法

這個方法接下來還會看施敢,這里我們只看現(xiàn)在需要的部分。

//ServiceMethod.java
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
  ...
  //1
  return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
復(fù)制代碼

返回的是一個HttpServiceMethod對象狭莱,那么接下來看下它的 invoke 方法

7.3.HttpServiceMethod 的 invoke 方法

//HttpServiceMethod.java
@Override
final @Nullable ReturnT invoke(Object[] args) {
  //1
  Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
  //2
  return adapt(call, args);
}
復(fù)制代碼

注釋 1:創(chuàng)建了一個Call對象僵娃,是 OkHttpCall,這個不就是在 GitHubApiService 這個接口聲明的 Call 對象嗎腋妙?

然后再看 OkHttpCall 的enqueue方法默怨,不就知道是怎么進(jìn)行請求,怎么回調(diào)的了嗎骤素?

注釋 2:是一個 adapt 方法匙睹,在不使用 Kotlin 協(xié)程的情況下愚屁,其實(shí)調(diào)用的是子類 CallAdapted 的 adapt,這個會在下面具體分析痕檬,包括 Kotlin 協(xié)程的 suspend 函數(shù)

現(xiàn)在我們已經(jīng)知道了 GitHubApiService 接口中定義的 listRepos中的 Call 對象霎槐,是 OkHttpCall,接下里看OkHttpCall 的 enqueue 方法

8.OkHttpCall的enqueue方法

這段代碼比較長梦谜,但這個就是這個請求的關(guān)鍵丘跌,以及怎么使用 OkHttp 進(jìn)行請求的,如果解析 Response 的唁桩,如何回調(diào)的闭树。

//OkHttpCall.java
@Override
public 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
          }
        }
      });
}
復(fù)制代碼

注釋 1:聲明一個 okhttp3.Call 對象,用來進(jìn)行網(wǎng)絡(luò)請求

注釋 2:給 okhttp3.Call 對象進(jìn)行賦值荒澡,下面會具體看代碼报辱,如何創(chuàng)建了一個 okhttp3.Call 對象

注釋 3:調(diào)用 okhttp3.Call 的 enqueue 方法,進(jìn)行真正的網(wǎng)絡(luò)請求

注釋 4:解析響應(yīng)仰猖,下面會具體看代碼

注釋 5:成功的回調(diào)

注釋 6:失敗的回調(diào)

到現(xiàn)在捏肢,我們文章開頭兩個疑問得到解釋了

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

底層是如何用 OkHttp 請求的饥侵?

我們還要看下一個 okhttp3.Call 對象是怎么創(chuàng)建的鸵赫,我們寫的注解參數(shù)是怎么解析的,響應(yīng)結(jié)果是如何解析的躏升,也就是我們在 Retrofit 中配置 addConverterFactory(GsonConverterFactory.create())是如何直接拿到數(shù)據(jù)模型的辩棒。

8.1.okhttp3.Call 對象是怎么創(chuàng)建的

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

//OkHttpCall.java
private 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;
}
復(fù)制代碼

通過 callFactory 創(chuàng)建的(callFactory應(yīng)該是 OkHttpClient),看一下 callFactory 的賦值過程

//OkHttpCall.java
OkHttpCall(
    RequestFactory requestFactory,
    Object[] args,
    okhttp3.Call.Factory callFactory,
    Converter<ResponseBody, T> responseConverter) {
  this.requestFactory = requestFactory;
  this.args = args;
  //通過 OkHttpCall 構(gòu)造直接賦值
  this.callFactory = callFactory;
  this.responseConverter = responseConverter;
}
復(fù)制代碼

在 OkHttpCall 構(gòu)造中直接賦值膨疏,那接下來就繼續(xù)看 OkHttpCall 的初始化過程

//HttpServiceMethod.java
private final okhttp3.Call.Factory callFactory;

@Override
final @Nullable ReturnT invoke(Object[] args) {
  //在 OkHttpCall 實(shí)例化時賦值一睁, callFactory 是 HttpServiceMethod 的成員變量
  Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
  return adapt(call, args);
}

//callFactory 是在 HttpServiceMethod 的構(gòu)造中賦值的
HttpServiceMethod(
    RequestFactory requestFactory,
    okhttp3.Call.Factory callFactory,
    Converter<ResponseBody, ResponseT> responseConverter) {
  this.requestFactory = requestFactory;
    //通過 HttpServiceMethod 構(gòu)造直接賦值
  this.callFactory = callFactory;
  this.responseConverter = responseConverter;
}
復(fù)制代碼

發(fā)現(xiàn) callFactory 的值是在創(chuàng)建 HttpServiceMethod 時賦值的,繼續(xù)跟佃却!

在 7.2 小節(jié)者吁,有一行代碼HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);我們沒有跟進(jìn)去,現(xiàn)在看一下 HttpServiceMethod 是怎么創(chuàng)建的

//HttpServiceMethod.java
static <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);
  }
}
復(fù)制代碼

注釋 1:callFactory 的值是從 Retrofit 這個對象拿到的

注釋 2:如果不是 Kotlin 的掛起函數(shù)饲帅,返回是的 CallAdapted 對象

static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {}
復(fù)制代碼

CallAdapted 是 HttpServiceMethod 的子類复凳,會調(diào)用 adapt方法進(jìn)行 CallAdapter 的轉(zhuǎn)換,我們后面會詳細(xì)看灶泵。

繼續(xù)看 Retrofit 的 callFactory 的值Retrofit是通過Builder構(gòu)建的育八,看下Builder類

//Retrofit.java
public 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);
    }

}
復(fù)制代碼

原來 callFactory 實(shí)際是一個 OkHttpClient 對象,也就是 OkHttpClient 創(chuàng)建了一個 Call 對象赦邻,嗯就是 OKHttp 網(wǎng)絡(luò)請求的那一套髓棋。

在創(chuàng)建okhttp3.Call 對象的 callFactory.newCall(requestFactory.create(args));方法中的 requestFactory.create(args)方法會返回一個 Request 的對象,這個我們也會在下面看是如何構(gòu)造一個 OkHttp 的 Request 請求對象的。

8.2.請求注解參數(shù)是怎么解析的

ServiceMethod.parseAnnotations(this, method);方法

//ServiceMethod.java
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
  //1
  RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
    ...
  //2
  return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
復(fù)制代碼

注釋 1:通過 RequestFactory 解析注解按声,然后返回 RequestFactory 對象

注釋 2:把 RequestFactory 對象往 HttpServiceMethod 里面?zhèn)鬟f膳犹,下面會具體看 RequestFactory 對象具體干什么用了?

繼續(xù)跟代碼RequestFactory.parseAnnotations

//RequestFactory.java
final 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);
  }
}
復(fù)制代碼

遍歷 GitHubApiService 這個 API 接口上定義的方法注解儒喊,然后解析注解

繼續(xù)跟代碼parseMethodAnnotation

//RequestFactory.java
private 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;
  }
}
復(fù)制代碼

就是解析方法上的注解镣奋,來存到 RequestFactory 的內(nèi)部币呵。

其實(shí) RequestFactory 這個類還有 parseParameterparseParameterAnnotation這個就是解析方法參數(shù)聲明上的具體參數(shù)的注解怀愧,會在后面分析 Kotlin suspend 掛起函數(shù)具體講。

總之:具體代碼就是分析方法上注解上面的值余赢,方法參數(shù)上芯义,這個就是細(xì)節(jié)問題了

總結(jié)就是:分析方法上的各個注解,方法參數(shù)上的注解妻柒,最后返回 RequestFactory 對象扛拨,給下面使用。

Retrofit 的大框架簡單举塔,細(xì)節(jié)比較復(fù)雜绑警。

RequestFactory 對象返回出去,具體干嘛用了央渣?大膽猜一下计盒,解析出注解存到 RequestFactory 對象,這個對象身上可有各種請求的參數(shù)芽丹,然后肯定是類創(chuàng)建 OkHttp 的 Request請求對象啊北启,因?yàn)槭怯?OkHttp 請求的,它需要一個 Request 請求對象

8.3.RequestFactory 對象返回出去拔第,具體干嘛用了?

下面我就用一個代碼塊貼了咕村,看著更直接,我會具體表明屬于哪個類的

//ServiceMethod.java
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
  //解析注解參數(shù)蚊俺,獲取 RequestFactory 對象
  RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
  //把 RequestFactory 對象傳給 HttpServiceMethod
  return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}

//注意換類了
//HttpServiceMethod.java
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
    Retrofit retrofit, Method method, RequestFactory requestFactory) {

    ...

  okhttp3.Call.Factory callFactory = retrofit.callFactory;
  //不是 Kotlin 的掛起函數(shù)
  if (!isKotlinSuspendFunction) {
    //把requestFactory傳給 CallAdapted
    return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
  } 
  ....
}

//HttpServiceMethod.java
//CallAdapted 是 HttpServiceMethod 的內(nèi)部類也是 HttpServiceMethod 的子類
CallAdapted(
    RequestFactory requestFactory,
    okhttp3.Call.Factory callFactory,
    Converter<ResponseBody, ResponseT> responseConverter,
    CallAdapter<ResponseT, ReturnT> callAdapter) {
  //這里把 requestFactory 傳給 super 父類的構(gòu)造參數(shù)里了懈涛,也就是 HttpServiceMethod
  super(requestFactory, callFactory, responseConverter);
  this.callAdapter = callAdapter;
}

//HttpServiceMethod.java
HttpServiceMethod(
    RequestFactory requestFactory,
    okhttp3.Call.Factory callFactory,
    Converter<ResponseBody, ResponseT> responseConverter) {
  // HttpServiceMethod 的 requestFactory 成員變量保存這個 RequestFactory 對象
  this.requestFactory = requestFactory;
  this.callFactory = callFactory;
  this.responseConverter = responseConverter;
}

//因?yàn)闀{(diào)用  HttpServiceMethod 的 invoke 方法
//會把這個 RequestFactory 對象會繼續(xù)傳遞給 OkHttpCall 類中
//注意換類了
//OkHttpCall.java
OkHttpCall(
    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;
}

復(fù)制代碼

經(jīng)過層層傳遞 RequestFactory 這個實(shí)例終于是到了 HttpServiceMethod 類中,最終傳到了 OkHttpCall 中泳猬,那這個 RequestFactory 對象在什么時候使用呢批钠? RequestFactory 會繼續(xù)在OkHttpCall中傳遞,因?yàn)?OkHttpCall 才是進(jìn)行請求的暂殖。

在OkHttpCall的 創(chuàng)建 Call 對象時

//OkHttpCall.java
private 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;
}
復(fù)制代碼

注釋 1:調(diào)用了requestFactory.create(args)

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

//RequestFactory.java
okhttp3.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();
}
復(fù)制代碼

最終 requestFactory 的值用來構(gòu)造 okhttp3.Request 的對象

以上就是解析注解价匠,構(gòu)造出okhttp3.Request的對象全過程了。

也就解答了方法上的注解是什么時候解析的呛每,怎么解析的踩窖?這個問題

8.4.請求響應(yīng)結(jié)果是如何解析的

比如我們在構(gòu)造 Retrofit 的時候加上 addConverterFactory(GsonConverterFactory.create())這行代碼,我們的響應(yīng)結(jié)果是如何通過 Gson 直接解析成數(shù)據(jù)模型的晨横?

在 OkHttpCall 的enqueue方法中

//OkHttpCall.java
@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 {
            //1 解析響應(yīng)
            response = parseResponse(rawResponse);
          } catch (Throwable e) {
            throwIfFatal(e);
            callFailure(e);
            return;
          }
        }
    ...
      });
}
復(fù)制代碼

注釋 1:通過parseResponse解析響應(yīng)返回給回調(diào)接口

//OkHttpCall.java
private 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 轉(zhuǎn)換 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;
  }
}
復(fù)制代碼

注釋 1:通過 responseConverter 調(diào)用convert方法

首先那看 responseConverter 是什么以及賦值的過程洋腮,然后再看convert方法

//OkHttpCall.java
private 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;
  //在構(gòu)造中賦值
  this.responseConverter = responseConverter;
}

// OkHttpCall 在 HttpServiceMethod 類中實(shí)例化
//注意換類了
//HttpServiceMethod.java
private final Converter<ResponseBody, ResponseT> responseConverter;

HttpServiceMethod(
    RequestFactory requestFactory,
    okhttp3.Call.Factory callFactory,
    Converter<ResponseBody, ResponseT> responseConverter) {
  this.requestFactory = requestFactory;
  this.callFactory = callFactory;
   //在構(gòu)造中賦值
  this.responseConverter = responseConverter;
}

//HttpServiceMethod 在子類 CallAdapted 調(diào)用 super方法賦值
CallAdapted(
    RequestFactory requestFactory,
    okhttp3.Call.Factory callFactory,
    Converter<ResponseBody, ResponseT> responseConverter,
    CallAdapter<ResponseT, ReturnT> callAdapter) {
  //在CallAdapted中調(diào)用super賦值
  super(requestFactory, callFactory, responseConverter);
  this.callAdapter = callAdapter;
}

復(fù)制代碼

繼續(xù)看 CallAdapted 的初始化中 responseConverter 的賦值過程

//HttpServiceMethod.java
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
    Retrofit retrofit, Method method, RequestFactory requestFactory) {
   ...
  CallAdapter<ResponseT, ReturnT> callAdapter =
      createCallAdapter(retrofit, method, adapterType, annotations);

  //1 實(shí)例化responseConverter
  Converter<ResponseBody, ResponseT> responseConverter =
      createResponseConverter(retrofit, method, responseType);

  okhttp3.Call.Factory callFactory = retrofit.callFactory;
  if (!isKotlinSuspendFunction) {
    //2 CallAdapted的實(shí)例化賦值
    return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
  } 
  ...
}
復(fù)制代碼

繼續(xù)跟代碼 createResponseConverter方法

//HttpServiceMethod.java
private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
    Retrofit retrofit, Method method, Type responseType) {
  Annotation[] annotations = method.getAnnotations();
  try {
    //調(diào)用的是 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.java
final List<Converter.Factory> converterFactories;

public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
  //繼續(xù)跟 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;
    }
  }
  ...
}

復(fù)制代碼

注釋 1:從 converterFactories 遍歷取出一個來調(diào)用 responseBodyConverter 方法箫柳,注意根據(jù) responseType 返回值類型來取到對應(yīng)的 Converter,如果不為空啥供,直接返回此 Converter 對象

看一下 converterFactories 這個對象的賦值過程

//Retrofit.java
final 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 的構(gòu)造賦值悯恍,Retrofit的 初始化是通過內(nèi)部 Builder 類的build方法
  this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.
  this.callbackExecutor = callbackExecutor;
  this.validateEagerly = validateEagerly;
}

//Retrofit.java 內(nèi)部類 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);
    }
復(fù)制代碼

注釋 1:初始化 converterFactories 這個 list

注釋 2:添加默認(rèn)的構(gòu)建的轉(zhuǎn)換器,其實(shí)是 StreamingResponseBodyConverter 和 BufferingResponseBodyConverter

注釋 3:就是自己添加的轉(zhuǎn)換配置 addConverterFactory(GsonConverterFactory.create())

//Retrofit.java 內(nèi)部類 Builder.java
public Builder addConverterFactory(Converter.Factory factory) {
  converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
  return this;
}
復(fù)制代碼

注釋 4:如果是 Java8 就是一個 OptionalConverterFactory 的轉(zhuǎn)換器否則就是一個空的

注意:是怎么找到GsonConverterFactory來調(diào)用 Gson 的 convert方法的呢伙狐?在遍歷converterFactories時會根據(jù) responseType來找到對應(yīng)的轉(zhuǎn)換器涮毫。

具體 GsonConverterFactory 的 convert 方法就是 Gson 的邏輯了,就不是 Retrofit 的重點(diǎn)了贷屎。

到現(xiàn)在Converter 的轉(zhuǎn)換過程罢防,我們也就清楚了。

還有一個問題唉侄,我們寫的 API 接口是如何支持 RxJava 的

9.CallAdapter的替換過程

9.1.使用 RxJava 進(jìn)行網(wǎng)絡(luò)請求

怎么轉(zhuǎn)成 RxJava

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

//初始化一個Retrofit對象
val retrofit = Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    //加入 RxJava2CallAdapterFactory 支持
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .addConverterFactory(GsonConverterFactory.create())
    .build()
復(fù)制代碼

加入 RxJava2 的配置支持后咒吐,把 RxJava2CallAdapterFactory 存到 callAdapterFactories 這個集合中,記住這一點(diǎn)属划,下面要用到恬叹。

interface GitHubApiService {
    @GET("users/{user}/repos")
    fun listReposRx(@Path("user") user: String?): Single<Repo>
}
復(fù)制代碼

我們就可以這么請求接口了

//創(chuàng)建出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()
    })
復(fù)制代碼

我們可以在自己定義的 API 接口中直接返回一個 RxJava 的 Single 對象的,來進(jìn)行操作了同眯。

我們下面就來看下是如何把請求對象轉(zhuǎn)換成一個 Single 對象的

//Retrofit.java 內(nèi)部類 Builder.java
public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
  callAdapterFactories.add(Objects.requireNonNull(factory, "factory == null"));
  return this;
}
復(fù)制代碼

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

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

這就要看我們之前看到了一個類了 HttpServiceMethod 的parseAnnotations之前看過它的代碼,只是上次看的是Converter是如何賦值的也就是第 8.4 小節(jié)嗽测,這次看 CallAdapter 是如何被賦值使用的绪励。

9.2CallAdapter是如何被賦值過程

HttpServiceMethod的parseAnnotations方法

//HttpServiceMethod.java
static <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);
  } 
    ...
}
復(fù)制代碼

注釋 1:初始化 CallAdapter

注釋 2:給 CallAdapted 中的 callAdapter 變量賦值,然后調(diào)用它的adapt 方法唠粥。

我們先找到具體 CallAdapter 賦值的對象疏魏,然后看它的adapt就知道了,是如何轉(zhuǎn)換的了

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

//HttpServiceMethod.java
private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
    Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
  try {
    //noinspection unchecked
    //調(diào)用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.java
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
  //調(diào)用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;
    }
  }
  ...
}

復(fù)制代碼

遍歷 callAdapterFactories 根據(jù) returnType類型 來找到對應(yīng)的 CallAdapter 返回

比如:我們在 GitHubApiService 的 returnType 類型為 Single晤愧,那么返回的就是 RxJava2CallAdapterFactory 所獲取的 CallAdapter

interface GitHubApiService {
    @GET("users/{user}/repos")
    fun listReposRx(@Path("user") user: String?): Single<Repo>
}
復(fù)制代碼

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;
  //當(dāng)前是Single類型
  boolean isSingle = rawType == Single.class;
  boolean isMaybe = rawType == Maybe.class;
  if (rawType != Observable.class && !isFlowable && !isSingle && !isMaybe) {
    return null;
  }
  ...
    //返回 RxJava2CallAdapter對象大莫,isSingle參數(shù)為 true
  return new RxJava2CallAdapter(responseType, scheduler, isAsync, isResult, isBody, isFlowable,
      isSingle, isMaybe, false);
}
復(fù)制代碼

返回的是 RxJava2CallAdapter 對象,并且根據(jù) rawType 判斷當(dāng)前是個什么類型

看下 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);
}
復(fù)制代碼

注釋 1:把 Call 包裝成一個 Observable 對象

注釋2:如果是 Single 則調(diào)用observable.singleOrError();方法

到目前為止官份,CallAdapter 怎么變成一個 RxJava2CallAdapter 以及它的具體調(diào)用只厘,我們也就清楚了。

10.Retrofit 如何支持 Kotlin 協(xié)程的 suspend 掛起函數(shù)的舅巷?

整個流程中還有一點(diǎn)我們沒有分析 Retrofit 如何支持 Kotlin 協(xié)程的 suspend 掛起函數(shù)的羔味?

首先寫一個 Demo 來看一下協(xié)程是怎么進(jìn)行網(wǎng)絡(luò)請求的

10.1.Kotlin 協(xié)程請求網(wǎng)絡(luò)的 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'
復(fù)制代碼

定義請求接口,寫一個掛起函數(shù)

interface GitHubApiService {
    //使用 Kotlin 協(xié)程 钠右,定義一個掛起函數(shù)
    @GET("users/{user}/repos")
    suspend fun listReposKt(@Path("user") user: String?): List<Repo>
}
復(fù)制代碼

請求接口

//創(chuàng)建出GitHubApiService對象
val service = retrofit.create(GitHubApiService::class.java)
//lifecycle提供的協(xié)程的Scope赋元,因?yàn)?suspend 函數(shù)需要運(yùn)行在協(xié)程里面
lifecycleScope.launchWhenResumed {
    try {
        val repo = service.listReposKt("octocat")
        "response name = ${repo[0].name}".logE()
    } catch (e: Exception) {
        e.printStackTrace()
        //出錯邏輯
        //ignore
    }
}
復(fù)制代碼

以上就是一個,用 Kotlin 協(xié)程進(jìn)行網(wǎng)絡(luò)請求的,Retrofit 是支持 Kotlin 協(xié)程的搁凸,接下來看下媚值,Retrofit 是怎么支持的。

10.2.分析Kotlin 協(xié)程的掛起函數(shù)的準(zhǔn)備工作

首先在開始之前护糖,我們得先得從代碼角度知道褥芒,Kotlin 的 suspend 函數(shù)對應(yīng)的 Java 類是什么樣子,不然嫡良,就一個 suspend 關(guān)鍵字根本就沒法進(jìn)行分析锰扶。

我寫一個 suspend 的測試方法,然后轉(zhuǎn)換成 java 方法看一下皆刺,這個 suspend 函數(shù)是個啥少辣。

寫一個 Top Level 的Suspend.kt文件(在文章最后我會給出源碼,一看就明白)

在文件中寫了一個測試的 suspend 函數(shù)

suspend fun test(name: String) {

}
復(fù)制代碼

我們通過 Android Studio 再帶的工具羡蛾,如下圖:把 Kotlin 方法轉(zhuǎn)成 Java 方法

點(diǎn)這個按鈕

結(jié)果如下

public final class SuspendKt {
   @Nullable
   public static final Object test(@NotNull String name, @NotNull Continuation $completion) {
      return Unit.INSTANCE;
   }
}
復(fù)制代碼

看到了,我們的 suspend 的關(guān)鍵字锨亏,變成了 test 方法的一個Continuation參數(shù)痴怨,且為最后一個參數(shù)

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

@SinceKotlin("1.3")
public interface Continuation<in T> {
    public val context: CoroutineContext
    public fun resumeWith(result: Result<T>)
}
復(fù)制代碼

好目前的準(zhǔn)備工作都已經(jīng)完成浪藻,開始分析 Retrofit 是怎么支持 Kotlin 協(xié)程的掛起函數(shù)的。

10.3.Retrofit 是怎么支持 Kotlin 協(xié)程的掛起函數(shù)的乾翔。

經(jīng)過前面的源碼解讀爱葵,我們知道,最終會調(diào)用到 HttpServiceMethod 的 parseAnnotations 方法

10.3.1.我們再看下這個方法反浓,這次只看有關(guān)協(xié)程的部分
//HttpServiceMethod.java
static <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 掛起函數(shù)
  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);
  }
}
復(fù)制代碼

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

注釋 2:如果是 Kotlin 掛起函數(shù)雷则,進(jìn)入此代碼塊

注釋 3:把 continuationWantsResponse 賦值為 true

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

獲取 isKotlinSuspendFunction 的值的過程

10.3.2.看 requestFactory 的 isKotlinSuspendFunction 賦值

requestFactory 這個類月劈,我們之前分析過度迂,就是解析注解的,但是有一部分沒看猜揪,就是解析方法參數(shù)上的注解惭墓,這次就看下。

//RequestFactory.java
private @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 遍歷解析參數(shù)的注解而姐,就是 @Path @Query @Field 等注解腊凶,具體就不看了,不是協(xié)程的重點(diǎn)
          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 如果是協(xié)程 ,其實(shí)比的就是最后一個值
    if (allowContinuation) {
      try {
        //3 判斷參數(shù)類型是 Continuation吭狡,這個接口尖殃,前面在 10.2 小節(jié)寫 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;
}
復(fù)制代碼

注釋 1:遍歷解析參數(shù)的注解,就是 @Path @Query @Field 等注解划煮,具體就不看了送丰,不是協(xié)程的重點(diǎn)

注釋 2:如果是協(xié)程 ,其實(shí)比的就是最后一個值

注釋 3:判斷參數(shù)類型是 Continuation弛秋,這個接口器躏,前面在 10.2 小節(jié)寫 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);
    }
  }
}
復(fù)制代碼

注釋 1:調(diào)用 callAdapter 代理 call 方法

注釋 2:取出最后一個參數(shù)蟹略,強(qiáng)轉(zhuǎn)成 Continuation 類型登失,想想我們寫的 Demo

注釋 3:Call 的擴(kuò)展函數(shù)(Kotlin 的寫法)下面具體看下 awaitResponse

注釋 4:出現(xiàn)異常,拋出異常挖炬。所以我們要在代碼中揽浙,要主動 try catch,來處理錯誤

10.3.4.看一下Call的擴(kuò)展函數(shù)
//KotlinExtensions.kt
suspend fun <T> Call<T>.awaitResponse(): Response<T> {
  return suspendCancellableCoroutine { continuation ->
    continuation.invokeOnCancellation {
      cancel()
    }
        //調(diào)用 Call的enqueue方法
    enqueue(object : Callback<T> {
      override fun onResponse(call: Call<T>, response: Response<T>) {
        //成功回調(diào)
        continuation.resume(response)
      }

      override fun onFailure(call: Call<T>, t: Throwable) {
        //失敗回調(diào)
        continuation.resumeWithException(t)
      }
    })
  }
}
復(fù)制代碼

到現(xiàn)在意敛,整個用 Kotlin 協(xié)程的請求過程我們也就看完了馅巷。

11.總結(jié)

至此,整個 Retrofit 的整體流程就分析完了草姻,具體細(xì)節(jié)還需要好好研究钓猬,我們再總結(jié)一下,回答開始的問題

11.1.什么是動態(tài)代理撩独?

分兩點(diǎn)動態(tài)指的是在運(yùn)行期敞曹,而代理指的是實(shí)現(xiàn)了某個接口的具體類,稱之為代理综膀,會調(diào)用了 InvocationHandler 的 invoke方法澳迫。

Retrofit 中的動態(tài)代理:

  • 在代碼運(yùn)行中,會動態(tài)創(chuàng)建 GitHubApiService 接口的實(shí)現(xiàn)類僧须,作為代理對象纲刀,代理接口的方法
  • 在我們調(diào)用GitHubApiService 接口的實(shí)現(xiàn)類的 listRepos方法時,會調(diào)用了 InvocationHandler 的 invoke方法担平。
  • 本質(zhì)上是在運(yùn)行期示绊,生成了 GitHubApiService 接口的實(shí)現(xiàn)類,調(diào)用了 InvocationHandler 的 invoke方法暂论。

具體看第 6 節(jié)

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

  • 我們在調(diào)用 GitHubApiService 接口的 listRepos方法時面褐,會調(diào)用 InvocationHandler 的 invoke方法
  • 然后執(zhí)行 loadServiceMethod方法并返回一個 HttpServiceMethod 對象并調(diào)用它的 invoke方法
  • 然后執(zhí)行 OkHttpCall的 enqueue方法
  • 本質(zhì)執(zhí)行的是 okhttp3.Call 的 enqueue方法
  • 當(dāng)然這期間會解析方法上的注解,方法的參數(shù)注解取胎,拼成 okhttp3.Call 需要的 okhttp3.Request 對象
  • 然后通過 Converter 來解析返回的響應(yīng)數(shù)據(jù)展哭,并回調(diào) CallBack 接口

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

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

看下第 11.2小節(jié)的解釋吧

具體看第 8 節(jié)

11.4.方法上的注解是什么時候解析的,怎么解析的匪傍?

  • 在 ServiceMethod.parseAnnotations(this, method); 方法中開始的
  • 具體內(nèi)容是在 RequestFactory 類中您市,進(jìn)行解析注解的
  • 調(diào)用 RequestFactory.parseAnnotations(retrofit, method); 方法實(shí)現(xiàn)的

具體看第 8.2 小節(jié)

11.5.Converter 的轉(zhuǎn)換過程,怎么通過 Gson 轉(zhuǎn)成對應(yīng)的數(shù)據(jù)模型的役衡?

  • 通過成功回調(diào)的 parseResponse(rawResponse);方法開始
  • 通過 responseConverter 的 convert 方法
  • responseConverter 是通過 converterFactories 通過遍歷茵休,根據(jù)返回值類型來使用對應(yīng)的 Converter 解析

具體看第 8.4 小節(jié)

11.6.CallAdapter 的替換過程,怎么轉(zhuǎn)成 RxJava 進(jìn)行操作的手蝎?

  • 通過配置 addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 在 callAdapterFactories 這個 list 中添加 RxJava2CallAdapterFactory
  • 如果不是 Kotlin 掛起函數(shù)最終調(diào)用的是 CallAdapted 的 adapt方法
  • callAdapter 的實(shí)例是通過 callAdapterFactories 這個 list 通過遍歷榕莺,根據(jù)返回值類型來選擇合適的CallAdapter

具體看第 9 節(jié)

11.7.如何支持 Kotlin 協(xié)程的 suspend 掛起函數(shù)的?

  • 通過 RequestFactory 解析方法上的參數(shù)值來判斷是不是一個掛起函數(shù)棵介,并把 isKotlinSuspendFunction 變量置為 true
  • 根據(jù) isKotlinSuspendFunction 這個變量來判斷響應(yīng)類型是否是 Response 類型钉鸯,然后把continuationWantsResponse 置為 true
  • 根據(jù) continuationWantsResponse 這個變量,來返回 SuspendForResponse 對象
  • 并調(diào)用 SuspendForResponse 的 invoke 方法
  • 通過 Call 的擴(kuò)展函數(shù)邮辽,來調(diào)用 Call 的 enqueue方法
  • 通過協(xié)程來返回

具體看第 10 節(jié)

到此為止唠雕,這篇文章算寫完了,當(dāng)然還有很多具體細(xì)節(jié)沒有研究逆巍,但對 Retrofit 的各個方面都進(jìn)行了閱讀及塘。

作者:AboBack
鏈接:https://juejin.im/post/6869584323079569415

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市锐极,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌芳肌,老刑警劉巖灵再,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異亿笤,居然都是意外死亡翎迁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進(jìn)店門净薛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來汪榔,“玉大人,你說我怎么就攤上這事肃拜〕针纾” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵燃领,是天一觀的道長士聪。 經(jīng)常有香客問我,道長猛蔽,這世上最難降的妖魔是什么剥悟? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任灵寺,我火速辦了婚禮,結(jié)果婚禮上区岗,老公的妹妹穿的比我還像新娘略板。我一直安慰自己,他們只是感情好慈缔,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布叮称。 她就那樣靜靜地躺著,像睡著了一般胀糜。 火紅的嫁衣襯著肌膚如雪颅拦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天教藻,我揣著相機(jī)與錄音距帅,去河邊找鬼。 笑死括堤,一個胖子當(dāng)著我的面吹牛碌秸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播悄窃,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼讥电,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了轧抗?” 一聲冷哼從身側(cè)響起恩敌,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎横媚,沒想到半個月后纠炮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡灯蝴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年恢口,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片穷躁。...
    茶點(diǎn)故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡耕肩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出问潭,到底是詐尸還是另有隱情猿诸,我是刑警寧澤,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布睦授,位于F島的核電站两芳,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏去枷。R本人自食惡果不足惜怖辆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一是复、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧竖螃,春花似錦淑廊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至腻格,卻和暖如春画拾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背菜职。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工青抛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人酬核。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓蜜另,卻偏偏與公主長得像,于是被迫代替她去往敵國和親嫡意。 傳聞我的和親對象是個殘疾皇子举瑰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評論 2 351