Android深入理解源碼——Retrofit

聲明:原創(chuàng)作品春霍,轉載請注明出處http://www.reibang.com/p/2186d666b1ee

做Android開發(fā)的小伙伴應該對Retrofit這個庫非常熟悉,Retrofit是Android中最常用的一個網(wǎng)絡請求庫贴唇,如果你還沒用過這個庫可以上Retrofit的官網(wǎng)或者可以看我之前寫的一篇關于Retrofit使用介紹:點擊查看。今天主要總結分析下Retrofit的源碼實現(xiàn)钓账,當然現(xiàn)在網(wǎng)上也有大量的關于Retrofit源碼分析的文章垒手,之所以再寫一遍主要還是作為自己的總結以加深印象胡嘿。

1.使用簡介

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

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

首先我們創(chuàng)建一個Retrofit實例疏魏,這里用github提供的接口測試停做。

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

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

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

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

// 執(zhí)行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方法并傳入相關的參數(shù)得到一個Call對象蛉腌,接著調用這個Call的execute方法來執(zhí)行具體網(wǎng)絡請求,然后返回請求結構即貢獻者名單只厘。

以上就是Retrofit的簡單使用烙丛,接下來我們就深入源碼來看下它內部是怎么實現(xiàn)的。

2.源碼解析

首先看下Retrofit的初始化:

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

這段代碼很好理解羔味,就是通過builder模式來創(chuàng)建Retrofit對象河咽。另外上面還添加了一個ConverterFactory,這個是用來解析請求的結果介评,這里用Gson來解析。

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

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

通過調用上面創(chuàng)建的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類中定義的寒瓦,則直接執(zhí)行該方法
                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里面的動態(tài)代理模式,如果你還不了解什么是動態(tài)代理建議你可以先參考這篇文章坪仇,知道了什么是動態(tài)代理杂腰,上面的代碼還是比較好理解的。通過Java自帶的APIProxy.newProxyInstance方法可以動態(tài)創(chuàng)建出我們上面定義的Github接口的實例椅文,這個方法需要傳入接口的類加載器喂很、Class對象以及一個InvocationHandler對象,當我們調用接口中定義的方法皆刺,比如contributors這個方法少辣,根據(jù)動態(tài)代理性質,最終會調用到InvocationHandler對象的invoke方法羡蛾,這個方法會回調三個參數(shù):創(chuàng)建的代理對象漓帅、調用的方法對象Method、以及調用這個方法時傳入的參數(shù)數(shù)組痴怨。接著我們看invoke里面的代碼:如果調用的方法是Object中定義的方法忙干,則直接執(zhí)行該方法并返回。否則如果這個方法是平臺默認方法則會執(zhí)行該默認方法并返回浪藻,一般我們這里定義的都不是默認方法所以會執(zhí)行這句代碼loadServiceMethod(method).invoke(args)捐迫,這句代碼通過loadServiceMethod方法返回一個ServiceMethod實例,然后調用這個對象的invoke方法爱葵,所以接下來我們來看下loadServiceMethod方法內部實現(xiàn):

  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方法是一個靜態(tài)方法懈玻,并且ServiceMethod是一個抽象類,那么這個方法最后返回的一定是ServiceMethod的一個實現(xiàn)類對象乾颁,我們來看下里面的具體實現(xiàn)涂乌,里面邏輯也是很清晰,首先通過RequestFactory的parseAnnotations方法來創(chuàng)建一個RequestFactory對象英岭,然后根據(jù)這個RequestFactory對象和其他一些參數(shù)通過HttpServiceMethod類的parseAnnotations方法來創(chuàng)建一個ServiceMethod的實現(xiàn)類對象湾盒。我們分別來看下這幾個方法的實現(xiàn),首先來看下RequestFactory的parseAnnotations方法:

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

首先parseAnnotations是一個靜態(tài)方法诅妹,里面通過Builder來創(chuàng)建具體的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的方法注解尖殃、參數(shù)類型以及參數(shù)注解數(shù)組

注:由于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);
    }

這個方法里面看上去代碼很多缔俄,其實主要做的事情很簡單,就是解析方法注解和方法中參數(shù)的注解器躏,解析完成之后就創(chuàng)建RequestFactory對象返回俐载,其他都是一些判空或者錯誤處理。所以接下來分別看下是怎么進行方法注解和參數(shù)注解解析的登失。首先進行方法注解解析的代碼如下:

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;
      }
    }

可以看到上面代碼是根據(jù)不同的注解執(zhí)行不同的解析規(guī)則壁畸,這里就拿我我上面例子中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后面可以用?然后后面帶參數(shù)請求捏萍,這個Retrofit中鏈接后面不能這樣直接帶參數(shù)太抓,上面方法會有個判斷如果帶了參數(shù)就會拋出一個異常提示讓你用@Query注解來傳get的參數(shù)。除此之外就是屬性的賦值令杈,比如把@GET注解后的路徑賦值給relativeUrl走敌,然后如果@GET注解后路徑有可變參數(shù),就會調用parsePathParameters方法來解析path逗噩。parsePathParameters具體里面代碼實現(xiàn)就不展開來了掉丽,就是用一個正則表達式來解析。
上面是關于方法注解的解析异雁,接下來看下方法參數(shù)注解是怎么解析的:

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方法會把每個參數(shù)解析成一個ParameterHandler對象捶障,后續(xù)會用到這個對象,我們看下具體的解析方法:

    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對象纲刀,如果一個參數(shù)有多個注解就會拋出異常项炼。接著看下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對象返回拌禾。這樣我們的參數(shù)注解也解析完了。然后我們回到上面的build方法中展哭,參數(shù)注解解析完后就會創(chuàng)建一個RequestFactory對象返回湃窍,其實這個對象里就是對我們向服務器請求的數(shù)據(jù)做一層封裝闻蛀。到這build方法執(zhí)行完了,其實ServiceMethod的parseAnnotations方法中的第一句代碼執(zhí)行完了,即:

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

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

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

這句代碼調用HttpServiceMethod的parseAnnotations方法并根據(jù)上面我們創(chuàng)建的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);
    }
  }

上面代碼比較長循榆,我們挑重點分析下,首先會根據(jù)adapterType來生成一個CallAdapter對象墨坚,這個CallAdapter用到了適配器模式,如果你還不是很熟悉適配器模式可以參看這篇文章:Java 大白話講解設計模式之 -- 適配器模式映挂,在Retrofit中這個CallAdapter的作用就是把Retrofit中的Call轉為你想要的一個請求對象泽篮,比如你想要Retrofit與RxJava結合使用,那么需要把這個Call轉為Observable柑船,這個轉換工作就是由CallAdapter來做的帽撑,當然根據(jù)你要轉為不同的對象,這個CallAdapter也是不一樣的鞍时,如果是RxJava的話就需要一個RxJava的CallAdapter亏拉,另外CallAdapter是由CallAdapterFactory來創(chuàng)建的,不同的CallAdapter他的CallAdapterFactory自然也不一樣逆巍,所以在Retrofit中CallAdapterFactory由開發(fā)者自己傳入及塘,比如你要使用RxJava那在Retrofit的初始化配置中就要傳入RxJava2CallAdapterFactory,如下代碼:

 addCallAdapterFactory(RxJava2CallAdapterFactory.create())

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

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

如果不是kotlin協(xié)程(暫時先不談論kotlin協(xié)程這種情況锐极,因為我對協(xié)程也不是很熟練2333333)則直接創(chuàng)建一個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,我們再一開始分析動態(tài)代理的時候知道調用網(wǎng)絡請求接口的方法時最后都會轉換到執(zhí)行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);
  }

可以看到子類有具體實現(xiàn)肋层,方法中創(chuàng)建了一個OkHttpCall,其實看名字就可以看得出來這個OkHttpCall通過我們傳入的參數(shù)里面封裝了OkHttp的東西翎迁,由于篇幅有限就不展開看了栋猖,然后會調用adapt方法,在HttpServiceMethod類中這是一個抽象方法汪榔,所以在其子類實現(xiàn)也就是剛提到的CallAdapted類,在上面CallAdapted中adapt方法是調用了callAdapter對象的adapt方法蒲拉,也就是上面?zhèn)魅氲倪m配器對象,它會把這個原生的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);
      }
    };
  }

可以看到這個默認的工廠類會創(chuàng)建一個默認的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);
}

當執(zhí)行contributors方法后就會返回一個被觀察者Observable對象蚯根,有了這個就可以根據(jù)RxJava規(guī)則進行鏈式調用后众。
如果你之前沒看過Retrofit源碼你可能會感到比較好奇這是怎么做到和RxJava完美結合,現(xiàn)在我們看完源碼再來看下它是怎么實現(xiàn)颅拦,其實關鍵還是上面我們提到的CallAdapter蒂誉,我們就來看下RxJava的CallAdapter是怎么實現(xiàn)的,我們進入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也是實現(xiàn)了Retrofit中的CallAdapter接口右锨,所以我們主要看下它的adapt方法是怎么實現(xiàn)的,可以看到這個方法其實就對OkHttpCall做了層層封裝碌秸,最后封裝成Observable被觀察者對象返回绍移,當Observable發(fā)生訂閱時就會調用里面的OkHttpCall對象的具體請求操作,然后把請求結果回調給觀察者讥电。這樣Retrofit就和RxJava完美結合起來了蹂窖。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市恩敌,隨后出現(xiàn)的幾起案子恼策,更是在濱河造成了極大的恐慌,老刑警劉巖潮剪,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涣楷,死亡現(xiàn)場離奇詭異,居然都是意外死亡抗碰,警方通過查閱死者的電腦和手機狮斗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來弧蝇,“玉大人碳褒,你說我怎么就攤上這事】戳疲” “怎么了沙峻?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長两芳。 經(jīng)常有香客問我摔寨,道長,這世上最難降的妖魔是什么怖辆? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任是复,我火速辦了婚禮删顶,結果婚禮上,老公的妹妹穿的比我還像新娘淑廊。我一直安慰自己逗余,他們只是感情好,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布季惩。 她就那樣靜靜地躺著录粱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪画拾。 梳的紋絲不亂的頭發(fā)上关摇,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機與錄音碾阁,去河邊找鬼。 笑死些楣,一個胖子當著我的面吹牛脂凶,可吹牛的內容都是我干的。 我是一名探鬼主播愁茁,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蚕钦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鹅很?” 一聲冷哼從身側響起嘶居,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎促煮,沒想到半個月后邮屁,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡菠齿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年佑吝,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绳匀。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡芋忿,死狀恐怖,靈堂內的尸體忽然破棺而出疾棵,到底是詐尸還是另有隱情戈钢,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布是尔,位于F島的核電站殉了,受9級特大地震影響,放射性物質發(fā)生泄漏拟枚。R本人自食惡果不足惜宣渗,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一抖所、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧痕囱,春花似錦田轧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至帮掉,卻和暖如春弦悉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蟆炊。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工稽莉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人涩搓。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓污秆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親昧甘。 傳聞我的和親對象是個殘疾皇子良拼,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內容