Retrofit 原理探索

語言: CN / TW / HK

開啟掘金成長之旅!這是我參與「掘金日新計劃 · 12 月更文挑戰」的第20天,點選檢視活動詳情

我們先來回顧一下 Retrofit 的基本使用 groovy implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

```kotlin interface NetApi { @GET("/hotkey/json") fun getHotKey(): Call

companion object {
    private const val BASE_URL = "https://www.wanandroid.com/"
    fun createApi(): NetApi =
        Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create())
            .build().create(NetApi::class.java)
}

}

data class HotWords( val id: String, val name: String, )

data class Response( val errorCode: Int, val errorMsg: String, val data: List ) kotlin NetApi.createApi().getHotKey().enqueue(object : Callback { override fun onResponse(call: Call, response: retrofit2.Response) { Log.i(tag, "onResponse: ${response.body()?.data}") }

override fun onFailure(call: Call<Response>, t: Throwable) {
    Log.i(tag, "onFailure: ${t.message}")
}

}) ```

這樣一個基本的網路請求就搞定了,使用很簡潔,正是因為其內部使用了大量的設計模式和優秀的架構設計,才得以使其如此方便地進行網路請求,下面來一起瞧瞧 Retrofit 的原始碼吧~

Retrofit構建過程

使用了建造者模式通過內部靜態類 Builder 構建一個 Retrofit 例項,這裡列出了部分方法,其他類似。

```java public static final class Builder { private final Platform platform; // 網路請求工廠,工廠方法模式 private @Nullable okhttp3.Call.Factory callFactory; // 網路請求地址 private @Nullable HttpUrl baseUrl; // 資料轉換器工廠的集合 private final List converterFactories = new ArrayList<>(); // 網路請求介面卡工廠的集合,預設是 ExecutorCallAdapterFactory private final List callAdapterFactories = new ArrayList<>(); // 回撥方法執行器,用於切換執行緒 private @Nullable Executor callbackExecutor; // 一個開關,為 true 則會快取建立的 ServiceMethod private boolean validateEagerly;

...

public Builder baseUrl(String baseUrl) {
  Objects.requireNonNull(baseUrl, "baseUrl == null");
  return baseUrl(HttpUrl.get(baseUrl));
}

public Builder baseUrl(HttpUrl baseUrl) {
  Objects.requireNonNull(baseUrl, "baseUrl == null");
  List<String> pathSegments = baseUrl.pathSegments();
  if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
    throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
  }
  this.baseUrl = baseUrl;
  return this;
}
// 將一個含有 Gson 物件例項的 GsonConverterFactory 放入資料轉換器工廠
public Builder addConverterFactory(Converter.Factory factory) {
  converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
  return this;
}

...

} ``` 通過 build,我們上面 Builder 類中的引數物件都配置到了 Retrofit 物件中。

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

  okhttp3.Call.Factory callFactory = this.callFactory;
  if (callFactory == null) {
    callFactory = new OkHttpClient();
  }

  Executor callbackExecutor = this.callbackExecutor;
  if (callbackExecutor == null) {
    callbackExecutor = platform.defaultCallbackExecutor();
  }

  // Make a defensive copy of the adapters and add the default Call adapter.
  List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
  callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

  // Make a defensive copy of the converters.
  List<Converter.Factory> converterFactories =
      new ArrayList<>(
          1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

  // Add the built-in converter factory first. This prevents overriding its behavior but also
  // ensures correct behavior when using converters that consume all types.
  converterFactories.add(new BuiltInConverters());
  converterFactories.addAll(this.converterFactories);
  converterFactories.addAll(platform.defaultConverterFactories());

  return new Retrofit(
      callFactory,
      baseUrl,
      unmodifiableList(converterFactories),
      unmodifiableList(callAdapterFactories),
      callbackExecutor,
      validateEagerly);
}

```

建立網路請求介面例項過程

使用動態代理的方式拿到所有註解配置後,建立網路請求介面例項。 ```java public T create(final Class service) { validateServiceInterface(service); return (T) Proxy.newProxyInstance( service.getClassLoader(), new Class<?>[] {service}, new InvocationHandler() { private final Platform platform = Platform.get(); private final Object[] emptyArgs = new Object[0];

          @Override
          public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            args = args != null ? args : emptyArgs;
            return platform.isDefaultMethod(method)
                ? platform.invokeDefaultMethod(method, service, proxy, args)
                : loadServiceMethod(method).invoke(args);
          }
        });

} ``` 跟蹤 loadServiceMethod

```java ServiceMethod<?> loadServiceMethod(Method method) { ServiceMethod<?> result = serviceMethodCache.get(method); if (result != null) return result;

synchronized (serviceMethodCache) {
  result = serviceMethodCache.get(method);
  if (result == null) {
    result = ServiceMethod.parseAnnotations(this, method);
    serviceMethodCache.put(method, result);
  }
}
return result;

} parseAnnotations 解析註解配置得到 ServiceMethod,然後加入到 serviceMethodCache 快取中,是一個 ConcurrentHashMap 。java abstract class ServiceMethod { static ServiceMethod parseAnnotations(Retrofit retrofit, Method method) { RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
  throw methodError(
      method,
      "Method return type must not include a type variable or wildcard: %s",
      returnType);
}
if (returnType == void.class) {
  throw methodError(method, "Service methods cannot return void.");
}

return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);

}

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

通過 RequestFactory 的 parseAnnotations 方法,解析介面方法上的註解,然後封裝在 RequestFactory 物件中,將其返回,這個 RequestFactory,主要用於後續建立 OkHttp 請求所需要的 Request 物件。那後面的 HttpServiceMethod.parseAnnotations 又是幹什麼的呢?往下看。 ```java static HttpServiceMethod parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) { ...

okhttp3.Call.Factory callFactory = retrofit.callFactory; if (!isKotlinSuspendFunction) { return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); } else if (continuationWantsResponse) { //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object. return (HttpServiceMethod) new SuspendForResponse<>( requestFactory, callFactory, responseConverter, (CallAdapter>) callAdapter); } else { //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object. return (HttpServiceMethod) new SuspendForBody<>( requestFactory, callFactory, responseConverter, (CallAdapter>) callAdapter, continuationBodyNullable); } } 如果不是 kotlin suspend 函式,使用 CallAdapted 類,如果是 kotlin suspend 函式返回型別是 Response,則使用 SuspendForResponse 類,其餘情況使用 SuspendForBody,如 suspend 函式返回型別不是 Response 。一般情況下,我們使用的基本上是屬於其餘情況,我們來看下 SuspendForBody 類java static final class SuspendForBody extends HttpServiceMethod { private final CallAdapter> callAdapter; private final boolean isNullable; ...

@Override protected Object adapt(Call call, Object[] args) { call = callAdapter.adapt(call);

Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];

try {
  return isNullable
      ? KotlinExtensions.awaitNullable(call, continuation)
      : KotlinExtensions.await(call, continuation);
} catch (Exception e) {
  return KotlinExtensions.suspendAndThrow(e, continuation);
}

} } ``` 跟進 KotlinExtensions.awaitNullable,我們可以看到 SuspendForBody 會將 Response.body 作為協程掛起點的返回值。

```java suspend fun Call.await(): T? { return suspendCancellableCoroutine { continuation -> //協程取消是呼叫 cancel continuation.invokeOnCancellation { cancel() } enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { if (response.isSuccessful) { //繼續執行相應的協程,將 response.body 作為最後一個掛起點的返回值。 continuation.resume(response.body()) } else { continuation.resumeWithException(HttpException(response)) } }

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

} } ```

執行請求過程

```java public void enqueue(final Callback callback) { Objects.requireNonNull(callback, "callback == null");

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 {
      // 建立 OkHttp 的 Call 物件
      call = rawCall = createRawCall();
    } catch (Throwable t) {
      throwIfFatal(t);
      failure = creationFailure = t;
    }
  }
}

if (failure != null) {
  callback.onFailure(this, failure);
  return;
}

if (canceled) {
  call.cancel();
}

call.enqueue(
    new okhttp3.Callback() {
      @Override
      public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
          // 解析網路請求返回的資料
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          throwIfFatal(e);
          callFailure(e);
          return;
        }

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

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

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

} ```

```java Response parseResponse(okhttp3.Response rawResponse) throws IOException { ResponseBody rawBody = rawResponse.body();

// Remove the body's source (the only stateful object) so we can pass the response along.
rawResponse =
    rawResponse
        .newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();

int code = rawResponse.code();
// 根據響應返回的狀態碼進行處理 
if (code < 200 || code >= 300) {
  try {
    // Buffer the entire body to avoid future I/O.
    ResponseBody bufferedBody = Utils.buffer(rawBody);
    return Response.error(bufferedBody, rawResponse);
  } finally {
    rawBody.close();
  }
}

if (code == 204 || code == 205) {
  rawBody.close();
  return Response.success(null, rawResponse);
}
//包裝 RequestBody
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
  // 將響應體轉為 Java 物件
  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;
}

} ```

總結

使用建造者模式通過 Builder 構建一個 Retrofit 例項,Builder 類中的引數物件都配置到 Retrofit 物件中,然後使用 JDK 動態代理的方式拿到所有註解配置後,建立網路請求介面例項,生成 OkHttp 請求,通過 CallAdapterFactory 找到對應的執行器,比如 RxJava2CallAdapterFactory,通過 ConverterFactory 將返回資料解析成 JavaBean,使用者只需關心請求引數,內部實現由 Retrofit 封裝完成,底層請求還是基於 Okhttp 實現的。