Android深入理解源碼——Retrofit

聲明:原創作品,轉載請註明出處https://www.jianshu.com/p/2186d666b1ee

做Android開發的小夥伴應該對Retrofit這個庫非常熟悉,Retrofit是Android中最常用的一個網絡請求庫,如果你還沒用過這個庫可以上Retrofit的官網或者可以看我之前寫的一篇關於Retrofit使用介紹:點擊查看。今天主要總結分析下Retrofit的源碼實現,當然現在網上也有大量的關於Retrofit源碼分析的文章,之所以再寫一遍主要還是作爲自己的總結以加深印象。

1.使用簡介

在開始源碼分析之前我們還是簡單看下Retrofit的使用:

Retrofit retrofit =
        new Retrofit.Builder()
            .baseUrl("https://api.github.com")
            .addConverterFactory(GsonConverterFactory.create())
            .build();

首先我們創建一個Retrofit實例,這裏用github提供的接口測試。

public interface GitHub {
  @GET("/repos/{owner}/{repo}/contributors")
  Call<List<Contributor>> contributors(@Path("owner") String owner, @Path("repo") String repo);
}

上面我們定義了一個接口裏面有一個查詢GitHub上某個庫的貢獻者名單。

// 創建上面Github接口的實例
GitHub github = retrofit.create(GitHub.class);

// 創建一個Call用來查詢square用戶下retrofit庫的貢獻者名單
Call<List<Contributor>> call = github.contributors("square", "retrofit");

// 執行call的請求返回貢獻者名單
List<Contributor> contributors = call.execute().body();
for (Contributor contributor : contributors) {
  System.out.println(contributor.login + " (" + contributor.contributions + ")");
}

輸出結果:
=========
JakeWharton (1061)
swankjesse (280)
pforhan (48)
eburke (36)
NightlyNexus (29)
dnkoutso (26)
edenman (24)
loganj (17)
Noel-96 (16)
rcdickerson (14)
rjrjr (13)
adriancole (9)
holmes (8)
Jawnnypoo (8)
JayNewstrom (7)
kryali (7)
swanson (7)
crazybob (6)
danrice-square (5)
vanniktech (5)
Turbo87 (5)
naturalwarren (5)
guptasourabh04 (4)
artem-zinnatullin (3)
chriscizek (3)
codebutler (3)
icastell (3)
jjNford (3)
ojh102 (3)
f2prateek (3)

可以看到我們通過Retrofit的create方法將上面的GitHub接口實例化了,然後調用這個實例化後GitHub的contributors方法並傳入相關的參數得到一個Call對象,接着調用這個Call的execute方法來執行具體網絡請求,然後返回請求結構即貢獻者名單。

以上就是Retrofit的簡單使用,接下來我們就深入源碼來看下它內部是怎麼實現的。

2.源碼解析

首先看下Retrofit的初始化:

Retrofit retrofit =
        new Retrofit.Builder()
            .baseUrl(API_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

這段代碼很好理解,就是通過builder模式來創建Retrofit對象。另外上面還添加了一個ConverterFactory,這個是用來解析請求的結果,這裏用Gson來解析。

接着我們主要看下下面這句代碼:

GitHub github = retrofit.create(GitHub.class);

通過調用上面創建的retrofit對象的create方法可以把我們定義的GitHub接口實例化,你可能會比較好奇這個方法是如何做到這點,我們就進入這個create方法看下:

  public <T> T create(final Class<T> service) {
    validateServiceInterface(service);
    return (T)
        Proxy.newProxyInstance(
            service.getClassLoader(),
            new Class<?>[] {service},
            new InvocationHandler() {
              private final Platform platform = Platform.get();
              private final Object[] emptyArgs = new Object[0];

              @Override
              public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
                  throws Throwable {
                // 如果調用的方法是在Object類中定義的,則直接執行該方法
                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);
              }
            });
  }

裏面代碼你可能看的比較暈,這裏其實用到Java裏面的動態代理模式,如果你還不瞭解什麼是動態代理建議你可以先參考這篇文章,知道了什麼是動態代理,上面的代碼還是比較好理解的。通過Java自帶的APIProxy.newProxyInstance方法可以動態創建出我們上面定義的Github接口的實例,這個方法需要傳入接口的類加載器、Class對象以及一個InvocationHandler對象,當我們調用接口中定義的方法,比如contributors這個方法,根據動態代理性質,最終會調用到InvocationHandler對象的invoke方法,這個方法會回調三個參數:創建的代理對象、調用的方法對象Method、以及調用這個方法時傳入的參數數組。接着我們看invoke裏面的代碼:如果調用的方法是Object中定義的方法,則直接執行該方法並返回。否則如果這個方法是平臺默認方法則會執行該默認方法並返回,一般我們這裏定義的都不是默認方法所以會執行這句代碼loadServiceMethod(method).invoke(args),這句代碼通過loadServiceMethod方法返回一個ServiceMethod實例,然後調用這個對象的invoke方法,所以接下來我們來看下loadServiceMethod方法內部實現:

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

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

裏面的代碼邏輯還是比較清晰的,首先有一個ServiceMethod的緩存,一進來先判斷有無緩存,有的話就直接返回緩存中的ServiceMethod,沒有的話就會調用ServiceMethod的parseAnnotations方法把傳入的Method對象解析成ServiceMethod對象,然後將得到的ServiceMethod方法存入緩存並返回。所以接下來就有必要來看下parseAnnotations是如何將Method轉爲ServiceMethod的:

abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> 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);
}

可以看到parseAnnotations方法是一個靜態方法,並且ServiceMethod是一個抽象類,那麼這個方法最後返回的一定是ServiceMethod的一個實現類對象,我們來看下里面的具體實現,裏面邏輯也是很清晰,首先通過RequestFactory的parseAnnotations方法來創建一個RequestFactory對象,然後根據這個RequestFactory對象和其他一些參數通過HttpServiceMethod類的parseAnnotations方法來創建一個ServiceMethod的實現類對象。我們分別來看下這幾個方法的實現,首先來看下RequestFactory的parseAnnotations方法:

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

首先parseAnnotations是一個靜態方法,裏面通過Builder來創建具體的RequestFactory實例,我們分別看下Builder的構造方法和build方法:

Builder(Retrofit retrofit, Method method) {
  this.retrofit = retrofit;
  this.method = method;
  this.methodAnnotations = method.getAnnotations();
  this.parameterTypes = method.getGenericParameterTypes();
  this.parameterAnnotationsArray = method.getParameterAnnotations();
}

這裏沒什麼特別的東西,就是把傳入的retrofit對象和method對象賦值,然後得到method的方法註解、參數類型以及參數註解數組

注:由於Retrofit中主要是通過註解來配置,所以再接着分析代碼之前,你還需要對Java註解有一定的瞭解。

接下來看下build方法:

    RequestFactory build() {
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }

      if (httpMethod == null) {
        throw methodError(method, "HTTP method annotation is required (e.g., @GET, @POST, etc.).");
      }

      if (!hasBody) {
        if (isMultipart) {
          throw methodError(
              method,
              "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
        }
        if (isFormEncoded) {
          throw methodError(
              method,
              "FormUrlEncoded can only be specified on HTTP methods with "
                  + "request body (e.g., @POST).");
        }
      }

      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
        parameterHandlers[p] =
            parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
      }

      if (relativeUrl == null && !gotUrl) {
        throw methodError(method, "Missing either @%s URL or @Url parameter.", httpMethod);
      }
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError(method, "Non-body HTTP method cannot contain @Body.");
      }
      if (isFormEncoded && !gotField) {
        throw methodError(method, "Form-encoded method must contain at least one @Field.");
      }
      if (isMultipart && !gotPart) {
        throw methodError(method, "Multipart method must contain at least one @Part.");
      }

      return new RequestFactory(this);
    }

這個方法裏面看上去代碼很多,其實主要做的事情很簡單,就是解析方法註解和方法中參數的註解,解析完成之後就創建RequestFactory對象返回,其他都是一些判空或者錯誤處理。所以接下來分別看下是怎麼進行方法註解和參數註解解析的。首先進行方法註解解析的代碼如下:

for (Annotation annotation : methodAnnotations) {
  parseMethodAnnotation(annotation);
}

上面代碼遍歷方法上的所有註解,然後調用parseMethodAnnotation方法進行挨個解析,我們看下parseMethodAnnotation方法:

    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 HEAD) {
        parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
      } else if (annotation instanceof PATCH) {
        parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
      } else if (annotation instanceof POST) {
        parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
      } else if (annotation instanceof PUT) {
        parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
      } else if (annotation instanceof OPTIONS) {
        parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
      } else if (annotation instanceof HTTP) {
        HTTP http = (HTTP) annotation;
        parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
      } else if (annotation instanceof retrofit2.http.Headers) {
        String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
        if (headersToParse.length == 0) {
          throw methodError(method, "@Headers annotation is empty.");
        }
        headers = parseHeaders(headersToParse);
      } 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;
      }
    }

可以看到上面代碼是根據不同的註解執行不同的解析規則,這裏就拿我我上面例子中Github接口中定義的contributors方法舉例,這個方法上面的註解是@GET,所以我們就只看@GET註解的解析:

    private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
      if (this.httpMethod != null) {
        throw methodError(
            method,
            "Only one HTTP method is allowed. Found: %s and %s.",
            this.httpMethod,
            httpMethod);
      }
      this.httpMethod = httpMethod;
      this.hasBody = hasBody;

      if (value.isEmpty()) {
        return;
      }

      // Get the relative URL path and existing query string, if present.
      int question = value.indexOf('?');
      if (question != -1 && question < value.length() - 1) {
        // Ensure the query string does not have any named parameters.
        String queryParams = value.substring(question + 1);
        Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
        if (queryParamMatcher.find()) {
          throw methodError(
              method,
              "URL query string \"%s\" must not have replace block. "
                  + "For dynamic query parameters use @Query.",
              queryParams);
        }
      }

      this.relativeUrl = value;
      this.relativeUrlParamNames = parsePathParameters(value);
    }

我們知道一個get請求,url後面可以用?然後後面帶參數請求,這個Retrofit中鏈接後面不能這樣直接帶參數,上面方法會有個判斷如果帶了參數就會拋出一個異常提示讓你用@Query註解來傳get的參數。除此之外就是屬性的賦值,比如把@GET註解後的路徑賦值給relativeUrl,然後如果@GET註解後路徑有可變參數,就會調用parsePathParameters方法來解析path。parsePathParameters具體裏面代碼實現就不展開來了,就是用一個正則表達式來解析。
上面是關於方法註解的解析,接下來看下方法參數註解是怎麼解析的:

int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
  parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
}

上面代碼通過parseParameter方法會把每個參數解析成一個ParameterHandler對象,後續會用到這個對象,我們看下具體的解析方法:

    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 =
              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) {
        if (allowContinuation) {
          try {
            if (Utils.getRawType(parameterType) == Continuation.class) {
              isKotlinSuspendFunction = true;
              return null;
            }
          } catch (NoClassDefFoundError ignored) {
          }
        }
        throw parameterError(method, p, "No Retrofit annotation found.");
      }

      return result;
    }

這個裏面通過調用parseParameterAnnotation方法把註解解析成ParameterHandler對象,如果一個參數有多個註解就會拋出異常。接着看下parseParameterAnnotation方法,這個方法其實就是針對不同的註解進行不同的處理,由於方法比較長,下面就拿@PATH舉例子:

        validateResolvableType(p, type);
        if (gotQuery) {
          throw parameterError(method, p, "A @Path parameter must not come after a @Query.");
        }
        if (gotQueryName) {
          throw parameterError(method, p, "A @Path parameter must not come after a @QueryName.");
        }
        if (gotQueryMap) {
          throw parameterError(method, p, "A @Path parameter must not come after a @QueryMap.");
        }
        if (gotUrl) {
          throw parameterError(method, p, "@Path parameters may not be used with @Url.");
        }
        if (relativeUrl == null) {
          throw parameterError(
              method, p, "@Path can only be used with relative url on @%s", httpMethod);
        }
        gotPath = true;

        Path path = (Path) annotation;
        String name = path.value();
        validatePathName(p, name);

        Converter<?, String> converter = retrofit.stringConverter(type, annotations);
        return new ParameterHandler.Path<>(method, p, name, converter, path.encoded());

可以看到前面是一些異常的處理,後面就是解析@PATH註解中的值,然後封裝成一個ParameterHandler對象返回。這樣我們的參數註解也解析完了。然後我們回到上面的build方法中,參數註解解析完後就會創建一個RequestFactory對象返回,其實這個對象裏就是對我們向服務器請求的數據做一層封裝。到這build方法執行完了,其實ServiceMethod的parseAnnotations方法中的第一句代碼執行完了,即:

RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

接着我們看下面這句關鍵的代碼:

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

這句代碼調用HttpServiceMethod的parseAnnotations方法並根據上面我們創建的RequestFactory對象返回一個ServiceMethod對象,我們進入這個方法看下:

  static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
    boolean continuationWantsResponse = false;
    boolean continuationBodyNullable = false;

    Annotation[] annotations = method.getAnnotations();
    Type adapterType;
    if (isKotlinSuspendFunction) {
      Type[] parameterTypes = method.getGenericParameterTypes();
      Type responseType =
          Utils.getParameterLowerBound(
              0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
      if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
        // Unwrap the actual body type from Response<T>.
        responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
        continuationWantsResponse = true;
      } else {
        // TODO figure out if type is nullable or not
        // Metadata metadata = method.getDeclaringClass().getAnnotation(Metadata.class)
        // Find the entry for method
        // Determine if return type is nullable or not
      }

      adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
      annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
    } else {
      adapterType = method.getGenericReturnType();
    }

    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    Type responseType = callAdapter.responseType();
    if (responseType == okhttp3.Response.class) {
      throw methodError(
          method,
          "'"
              + getRawType(responseType).getName()
              + "' is not a valid response body type. Did you mean ResponseBody?");
    }
    if (responseType == Response.class) {
      throw methodError(method, "Response must include generic type (e.g., Response<String>)");
    }
    // TODO support Unit for Kotlin?
    if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
      throw methodError(method, "HEAD method must use Void as response type.");
    }

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

上面代碼比較長,我們挑重點分析下,首先會根據adapterType來生成一個CallAdapter對象,這個CallAdapter用到了適配器模式,如果你還不是很熟悉適配器模式可以參看這篇文章:Java 大白話講解設計模式之 -- 適配器模式,在Retrofit中這個CallAdapter的作用就是把Retrofit中的Call轉爲你想要的一個請求對象,比如你想要Retrofit與RxJava結合使用,那麼需要把這個Call轉爲Observable,這個轉換工作就是由CallAdapter來做的,當然根據你要轉爲不同的對象,這個CallAdapter也是不一樣的,如果是RxJava的話就需要一個RxJava的CallAdapter,另外CallAdapter是由CallAdapterFactory來創建的,不同的CallAdapter他的CallAdapterFactory自然也不一樣,所以在Retrofit中CallAdapterFactory由開發者自己傳入,比如你要使用RxJava那在Retrofit的初始化配置中就要傳入RxJava2CallAdapterFactory,如下代碼:

 addCallAdapterFactory(RxJava2CallAdapterFactory.create())

有了CallAdapter我們再接着往下看:

okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
  return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
}

如果不是kotlin協程(暫時先不談論kotlin協程這種情況,因爲我對協程也不是很熟練2333333)則直接創建一個CallAdapted對象返回,我們看下這個CallAdapted對象:

  static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
    private final CallAdapter<ResponseT, ReturnT> callAdapter;

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

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

可以看到這個CallAdapted是繼承自HttpServiceMethod類,而HttpServiceMethod類又是繼承自ServiceMethod,我們再一開始分析動態代理的時候知道調用網絡請求接口的方法時最後都會轉換到執行ServiceMethod的invoke方法:

loadServiceMethod(method).invoke(args);

所以我們看下ServiceMethod的invoke方法:

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

在ServiceMethod中這個invoke是個抽象方法,所以我們到他的子類HttpServiceMethod中看下:

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

可以看到子類有具體實現,方法中創建了一個OkHttpCall,其實看名字就可以看得出來這個OkHttpCall通過我們傳入的參數裏面封裝了OkHttp的東西,由於篇幅有限就不展開看了,然後會調用adapt方法,在HttpServiceMethod類中這是一個抽象方法,所以在其子類實現也就是剛提到的CallAdapted類,在上面CallAdapted中adapt方法是調用了callAdapter對象的adapt方法,也就是上面傳入的適配器對象,它會把這個原生的OkHttpCall轉爲所需要的對象。上面我們說過要轉成什麼對象需要自己傳入對應的CallAdapter的工廠類,但是上面例子中我們在初始化Retrofit時也沒有傳入這個工廠對象,其實如果不傳的話Retrofit會使用裏面默認的CallAdapterFactory對象,

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

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

  @Override
  public @Nullable CallAdapter<?, ?> get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    if (!(returnType instanceof ParameterizedType)) {
      throw new IllegalArgumentException(
          "Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
    }
    final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);

    final Executor executor =
        Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
            ? null
            : callbackExecutor;

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

      @Override
      public Call<Object> adapt(Call<Object> call) {
        return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
      }
    };
  }

可以看到這個默認的工廠類會創建一個默認的CallAdapter,而這個CallAdapter的adapt其實就是直接返回這個傳入的call對象,也就是上面的OkHttpCall,當然這裏還有個executor是否爲空的判斷,默認是爲空,這裏就不深入討論。

3.與RxJava結合使用

我們知道Retrofit是可以和RxJava結合使用的,之所以能結合我們上面也提到了,是通過CallAdapter把OkHttpCall轉爲Observable被觀察者對象。代碼上只需要在Retrofit初始化時傳入RxJavaCallAdapterFactory對象:

Retrofit retrofit =
        new Retrofit.Builder()
             .baseUrl(API_URL)
             .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
             .build();

接下來把接口方法返回的Call對象改爲Observale:

public interface GitHub {
  @GET("/repos/{owner}/{repo}/contributors")
  Observable<List<Contributor>> contributors(@Path("owner") String owner, @Path("repo") String repo);
}

當執行contributors方法後就會返回一個被觀察者Observable對象,有了這個就可以根據RxJava規則進行鏈式調用。
如果你之前沒看過Retrofit源碼你可能會感到比較好奇這是怎麼做到和RxJava完美結合,現在我們看完源碼再來看下它是怎麼實現,其實關鍵還是上面我們提到的CallAdapter,我們就來看下RxJava的CallAdapter是怎麼實現的,我們進入RxJavaCallAdapterFactory,然後最後我們會找到一個RxJavaCallAdapter:


final class RxJavaCallAdapter<R> implements CallAdapter<R, Object> {
  private final Type responseType;
  private final @Nullable Scheduler scheduler;
  private final boolean isAsync;
  private final boolean isResult;
  private final boolean isBody;
  private final boolean isSingle;
  private final boolean isCompletable;

  RxJavaCallAdapter(
      Type responseType,
      @Nullable Scheduler scheduler,
      boolean isAsync,
      boolean isResult,
      boolean isBody,
      boolean isSingle,
      boolean isCompletable) {
    this.responseType = responseType;
    this.scheduler = scheduler;
    this.isAsync = isAsync;
    this.isResult = isResult;
    this.isBody = isBody;
    this.isSingle = isSingle;
    this.isCompletable = isCompletable;
  }

  @Override
  public Type responseType() {
    return responseType;
  }

  @Override
  public Object adapt(Call<R> call) {
    OnSubscribe<Response<R>> callFunc =
        isAsync ? new CallEnqueueOnSubscribe<>(call) : new CallExecuteOnSubscribe<>(call);

    OnSubscribe<?> func;
    if (isResult) {
      func = new ResultOnSubscribe<>(callFunc);
    } else if (isBody) {
      func = new BodyOnSubscribe<>(callFunc);
    } else {
      func = callFunc;
    }
    Observable<?> observable = Observable.create(func);

    if (scheduler != null) {
      observable = observable.subscribeOn(scheduler);
    }

    if (isSingle) {
      return observable.toSingle();
    }
    if (isCompletable) {
      return observable.toCompletable();
    }
    return observable;
  }
}

可以看到這個RxJavaCallAdapter也是實現了Retrofit中的CallAdapter接口,所以我們主要看下它的adapt方法是怎麼實現的,可以看到這個方法其實就對OkHttpCall做了層層封裝,最後封裝成Observable被觀察者對象返回,當Observable發生訂閱時就會調用裏面的OkHttpCall對象的具體請求操作,然後把請求結果回調給觀察者。這樣Retrofit就和RxJava完美結合起來了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章