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完美结合起来了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容