Android 主流开源框架(五)Retrofit 源码解析

前言

最近有个想法——就是把 Android 主流开源框架进行深入分析,然后写成一系列文章,包括该框架的详细使用与源码解析。目的是通过鉴赏大神的源码来了解框架底层的原理,也就是做到不仅要知其然,还要知其所以然。

这里我说下自己阅读源码的经验,我一般都是按照平时使用某个框架或者某个系统源码的使用流程入手的,首先要知道怎么使用,然后再去深究每一步底层做了什么,用了哪些好的设计模式,为什么要这么设计。

系列文章:

更多干货请关注 AndroidNotes

一、Retrofit 的基本使用示例

1.1 同步请求

(1)创建网络请求接口:

public interface PostmanService {
    @GET("get")
    Call<PostmanGetBean> testGet();
}

(2)创建 Retrofit 的实例:

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://postman-echo.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();

(3)创建网络请求接口的实例,并调用接口中的方法获取 Call 对象:

PostmanService service = retrofit.create(PostmanService.class);
Call<PostmanGetBean> call = service.testGet();

(4)进行网络请求

Response<PostmanGetBean> response = call.execute();

1.2 异步请求

同步请求与异步请求唯一不同的就是第 (4) 步,前者使用同步方法 execute(),后者使用异步方法 enqueue()。异步请求的第(4)步如下:

call.enqueue(new Callback<PostmanGetBean>() {
    @Override
    public void onResponse(Call<PostmanGetBean> call, Response<PostmanGetBean> response) {
    }

    @Override
    public void onFailure(Call<PostmanGetBean> call, Throwable t) {

    }
});

更多 Retrofit 的使用方法可以看我之前写的文章 Retrofit 使用详解
接下来我们就根据这 4 步进行源码阅读。

二、Retrofit 源码分析

源码版本:2.5.0

2.1 (1)创建网络请求接口

public interface PostmanService {
    @GET("get")
    Call<PostmanGetBean> testGet();
}

这一步比较简单,只是创建一个接口,接口中包含一个带注解的方法。这里没有什么源码好讲的,我们放到后面结合第(3)步再一起讲。

2.2 (2)创建 Retrofit 的实例

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://postman-echo.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();

首先我们点击 Retrofit 对象进去有如下常量:

  /*Retrofit*/
  // ServiceMethod 缓存,用来存储网络请求相关的配置,例如网络请求的方法、数据转换器、网络请求适配器、网络请求工厂、基地址等
  private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
  // 网络请求器工厂
  final okhttp3.Call.Factory callFactory;
  // 网络请求的 url 地址
  final HttpUrl baseUrl;
  // 数据转换器工厂的集合
  final List<Converter.Factory> converterFactories;
  // 网络请求适配器工厂的集合
  final List<CallAdapter.Factory> callAdapterFactories;
  // 回调方法执行器
  final @Nullable Executor callbackExecutor;
  // 一个标志位,用来判断是否需要加载 ServiceMethod
  final boolean validateEagerly;

这些常量除了 serviceMethodCache,其余都可以通过建造者模式进行配置,例如 baseUrl 可以通过 baseUrl() 方法配置。

然后再看下 Builder() 方法:

    /*Retrofit-Builder*/
    Builder(Platform platform) {
      this.platform = platform;
    }
    public Builder() {
      this(Platform.get());
    }

Builder() 方法中调用了 Platform 类的 get() 方法,get() 方法中又调用了 findPlatform() 方法,如下:

  /*Platform*/
  private static final Platform PLATFORM = findPlatform();

  static Platform get() {
    return PLATFORM;
  }

  private static Platform findPlatform() {
    try {
      Class.forName("android.os.Build");
      if (Build.VERSION.SDK_INT != 0) {
        // Android 平台
        return new Android();
      }
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("java.util.Optional");
      // Java 平台
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
  }

可以看到,Builder() 方法主要是用来查找当前运行的平台,Retrofit 支持 Android 与 Java 平台。

最后再看看 build() 方法:

    /*Retrofit*/
    public Retrofit build() {
      //(1)
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
      //(2)
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }
      //(3)
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }
      //(4)
      // 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));
      //(5)star
      // 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());
      //(5)end
      //(6)
      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }

源码中我标注了 6 个关注点,分别如下:

  • (1):说明 baseUrl 是必须要设置的,否则报异常。
  • (2):如果没有设置 callFactory,则默认使用 OkHttpClient。
  • (3):如果没有设置 callbackExecutor,则使用当前运行平台的默认 callbackExecutor。
    这里点击 defaultCallbackExecutor() 方法进去是这样的:
  /*Platform*/
  @Nullable Executor defaultCallbackExecutor() {
    return null;
  }

由于运行在 Android 平台,所以跳转到 Platform 的实现类 Android 中的 defaultCallbackExecutor() 方法:

    /*Platform-Android*/
    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

继续跟进到 MainThreadExecutor() :

    /*Platform-Android*/
    static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override public void execute(Runnable r) {
        handler.post(r);
      }
    }

可以看到,这里通过创建一个主线程的 Handler 来将线程从子线程切换到主线程,这样后面从服务器拿到数据就是在主线程了,就可以直接进行 UI 相关操作了。

  • (4):配置 CallAdapter.Factory 的集合,首先往集合中添加客户端设置的,然后再添加当前平台默认的(CompletableFutureCallAdapterFactory 与 ExecutorCallAdapterFactory)。
  • (5):配置 Converter.Factory 的集合,首先往集合中添加 BuiltInConverters,然后添加客户端设置的(我们前面设置了 GsonConverterFactory),最后添加当前平台默认的(OptionalConverterFactory)。
  • (6):通过调用 Retrofit 的构造函数来完成 Retrofit 中常量的赋值。

小结:
这一步使用了建造者模式去创建 Retrofit 的实例,创建的过程中主要配置了网络请求器工厂(callFactory)、网络请求的 url 地址(baseUrl)、数据转换器工厂的集合(converterFactories)、网络请求适配器工厂的集合(callAdapterFactories)、回调方法执行器(callbackExecutor)。这里使用建造者模式的好处是不需要知道 Retrofit 内部是怎么创建的,只需要传入对应的配置即可创建非常复杂的对象。

2.3 (3)创建网络请求接口的实例,并调用接口中的方法获取 Call 对象

PostmanService service = retrofit.create(PostmanService.class);
Call<PostmanGetBean> call = service.testGet();

首先我们点击 create() 方法进去源码是这样的:

  /*Retrofit*/
  public <T> T create(final Class<T> service) {
    // 验证传进来的 service(service 必须是一个接口,并且不能继承其他接口,否则抛出异常)
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    //(1)Proxy.newProxyInstance()
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();
          private final Object[] emptyArgs = new Object[0];
          //(2)invoke()
          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            //(3)
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            //(4)
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            //(5)
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }
  • (1):这里使用动态代理(不了解动态代理的建议先去看下我写的文章 代理模式)来创建网络请求接口的实例并返回。newProxyInstance() 方法中的参数表示如下:
    service.getClassLoader():ClassLoader,被代理对象的类加载器。
    new Class<?>[] { service }:Class<?>[] ,被代理对象需要实现的全部接口。
    new InvocationHandler(){}:InvocationHandler,实现了 InvocationHandler 接口的对象。

注意:其实关注点(1)中执行 Proxy 的 newProxyInstance() 方法就已经创建了网络请求接口的实例, create() 方法的调用并不会继续执行关注点(2)中的 invoke() 方法。调用网络请求接口中的方法(例如例子中 service.testGet())的时候才会执行 invoke() 方法。

我们继续看例子中的第二行代码:

Call<PostmanGetBean> call = service.testGet();

这行代码的意思就是调用接口中的方法获取 Call 对象,其实这里调用 testGet() 方法才是刚开始执行上面动态代理中的 invoke() 方法,所以我们继续分析其他关注点吧!

  • (2):invoke() 方法为 InvocationHandler 接口需要重写的方法,该方法里面会通过 Java 反射机制调用被代理对象的方法。invoke() 方法中的参数表示如下:
    proxy:动态代理对象,对应例子中是 PostmanService。
    method:被代理对象的方法,对应例子中是 testGet()。
    args:被代理对象方法的参数,这里是 null,因为例子中 testGet() 方法没有传参数。
  • (3):如果该方法是 Object 的方法,例如 equals()、toString(),那就直接调用。
  • (4):如果该方法是 Java8 默认方法,则执行该平台的默认方法。
  • (5):如果前面 2 种都不符合,则会调用 loadServiceMethod() 方法得到 ServiceMethod 后再调用 invoke() 方法。

我们先点击 loadServiceMethod() 方法进去看看:

  /*Retrofit*/
  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() 方法创建,然后再存到缓存集合中。

我们点击 parseAnnotations() 方法进去看看:

  /*ServiceMethod*/
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    //(6)
    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.");
    }
    //(7)
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }
  • (6):调用 RequestFactory 的 parseAnnotations() 方法解析网络请求接口中的注解。
    点击 RequestFactory 的 parseAnnotations() 方法进去看看:
  /*RequestFactory*/
  static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
    return new Builder(retrofit, method).build();
  }

这里使用了建造者模式,Builder 的构造函数只是一些简单的赋值,如下:

    /*RequestFactory-Builder*/
    Builder(Retrofit retrofit, Method method) {
      // Retrofit 对象
      this.retrofit = retrofit;
      // 方法
      this.method = method;
      // 方法注解
      this.methodAnnotations = method.getAnnotations();
      // 参数类型
      this.parameterTypes = method.getGenericParameterTypes();
      // 参数注解数组
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }

继续看看 build() 方法:

    /*RequestFactory-Builder*/
    RequestFactory build() {
      //(6.1)
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
      // 省略部分判断...
      //(6.2)
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
        parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p]);
      }
      // 省略部分判断...
      //(6.3)
      return new RequestFactory(this);
    }

可以看到,关注点(6.1)中的 for 循环是用来解析方法上的注解,例如 @GET、@HTTP、@Headers 等等注解。关注点(6.2)中的 for 循环是用来解析方法上的参数的。执行到关注点(6.3)说明网络请求接口中的注解已经解析出来了,然后创建 RequestFactory 对象并进行赋值后返回,如下:

  /*RequestFactory*/
  RequestFactory(Builder builder) {
    // 方法,对应例子中是 testGet()
    method = builder.method;
    // baseUrl,对应例子中是 https://postman-echo.com/
    baseUrl = builder.retrofit.baseUrl;
    // http 方法,对应例子中是 GET 请求
    httpMethod = builder.httpMethod;
    // 相对路径,对应例子中是 get
    relativeUrl = builder.relativeUrl;
    // 请求头,没有设置,默认为 null
    headers = builder.headers;
    // contentType,没有设置,默认为 null
    contentType = builder.contentType;
    // hasBody,没有设置,默认为 false
    hasBody = builder.hasBody;
    // isFormEncoded,没有设置,默认为 false
    isFormEncoded = builder.isFormEncoded;
    // isMultipart,没有设置,默认为 false
    isMultipart = builder.isMultipart;
    // 参数数组,例子中没有传参数,所以为 null
    parameterHandlers = builder.parameterHandlers;
  }
  • (7):继续回到关注点(7),点击 HttpServiceMethod 中的 parseAnnotations() 方法进去看看:
  /*HttpServiceMethod*/
  static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    // 根据网络请求接口中的返回类型与注解获取相对应的网络请求适配器(CallAdapter)
    CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method);
    // 获取响应类型,对应例子中是 PostmanGetBean
    Type responseType = callAdapter.responseType();
    if (responseType == Response.class || responseType == okhttp3.Response.class) {
      throw methodError(method, "'"
          + Utils.getRawType(responseType).getName()
          + "' is not a valid response body type. Did you mean ResponseBody?");
    }
    if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
      throw methodError(method, "HEAD method must use Void as response type.");
    }
    // 根据网络请求接口中的返回类型与注解获取相对应的数据转换器(Converter)
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);
    // 获取网络请求器工厂(okhttp3.Call.Factory)
    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    return new HttpServiceMethod<>(requestFactory, callFactory, callAdapter, responseConverter);
  }

可以看到,该方法主要是用来获取相对应的网络请求适配器(CallAdapter)、数据转换器(Converter)与网络请求器工厂(okhttp3.Call.Factory),对应例子中分别是 ExecutorCallAdapterFactory、GsonConverterFactory 与 OkHttpClient。最后再通过这些来创建 HttpServiceMethod 对象进行返回,HttpServiceMethod 继承自 ServiceMethod,所以可以转成 ServiceMethod。

loadServiceMethod() 方法拿到 ServiceMethod 对象后继续回去看关注点(5)中剩下的:

/*Retrofit*/
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);

这里调用了 invoke() 方法,点击进去是调用了 ServiceMethod 的 invoke() 方法,然后在子类 HttpServiceMethod 中实现,具体如下:

  /*ServiceMethod*/
  abstract T invoke(Object[] args);
  
  /*HttpServiceMethod*/
  @Override ReturnT invoke(Object[] args) {
    return callAdapter.adapt(
        new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
  }

可以看到,这里调用了 CallAdapter 的 adapt() 方法,参数是创建了一个 OkHttpCall 对象,OkHttpCall 对象的创建只是一些简单的赋值,就不贴代码了。上面已经说了,CallAdapter 对应例子中就是 ExecutorCallAdapterFactory,所以点击 adapt() 方法进去看看:

    /*ExecutorCallAdapterFactory*/
    @Nullable
    public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        if (getRawType(returnType) != Call.class) {
            return null;
        } else {
            final Type responseType = Utils.getCallResponseType(returnType);
            return new CallAdapter<Object, Call<?>>() {
                public Type responseType() {
                    return responseType;
                }
                // 关注点
                public Call<Object> adapt(Call<Object> call) {
                    return new ExecutorCallAdapterFactory.ExecutorCallbackCall(ExecutorCallAdapterFactory.this.callbackExecutor, call);
                }
            };
        }
    }

可以看到关注点中 adapt() 方法返回的是 ExecutorCallbackCall,而 ExecutorCallbackCall 实现了 Call 接口,这样就获取了 Call 对象了,就可以愉快的调用 execute() 与 enqueue() 方法进行网络请求了。

小结:

  1. 首先通过调用 retrofit.create(),使用动态代理模式来创建网络请求接口的实例(对应例子中是 PostmanService)。
  2. 然后通过调用网络请求接口中的方法(对应例子中是 service.testGet())来执行动态代理中的 invoke() 方法。
  3. invoke() 方法中使用单例模式与建造者模式创建 ServiceMethod 对象,即如果创建过直接从缓存中获取,没有则重新创建。重新创建的过程会解析网络请求接口中方法上的注解、参数、获取返回类型等,也就是将网络请求接口中的方法解析成 HTTP 请求需要的各种参数。
  4. 最后通过调用 ServiceMethod 对象中的 invoke() 方法获取网络请求需要的 Call 对象。

这里使用动态代理的好处是可以将网络请求接口的所有方法的调用都会集中转发到 InvocationHandler 接口的 invoke() 方法中,方便集中进行处理。

2.4 (4)进行网络请求

进行网络请求可以分为同步请求与异步请求,下面分别对这两种请求方式进行分析。

2.4.1 同步请求

Response<PostmanGetBean> response = call.execute();

上面已经知道了 call 为 ExecutorCallbackCall 类型的 Call 接口,点击 execute() 方法进去看看:

    /*ExecutorCallAdapterFactory-ExecutorCallbackCall*/
    @Override public Response<T> execute() throws IOException {
      return delegate.execute();
    }

这里的 delegate 实际为 OkHttpCall,点击这里的 execute() 方法进去看看:

  /*OkHttpCall*/
  @Override public Response<T> execute() throws IOException {
    okhttp3.Call call;

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      if (creationFailure != null) {
        if (creationFailure instanceof IOException) {
          throw (IOException) creationFailure;
        } else if (creationFailure instanceof RuntimeException) {
          throw (RuntimeException) creationFailure;
        } else {
          throw (Error) creationFailure;
        }
      }

      call = rawCall;
      if (call == null) {
        try {
          //(1)
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException | Error e) {
          throwIfFatal(e); //  Do not assign a fatal error to creationFailure.
          creationFailure = e;
          throw e;
        }
      }
    }

    if (canceled) {
      call.cancel();
    }
    //(2)
    return parseResponse(call.execute());
  }
  • (1):这里创建了一个 okhttp3.Call 类型的 call,点击 createRawCall() 方法进去看看是怎么创建的:
  /*OkHttpCall*/
  private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

可以看到,这里通过 requestFactory.create(args) 构建一个 Request 对象,然后再通过 OkHttpClient 的 newCall() 方法去创建 call。关于 newCall() 怎么创建 call,这个在讲 Android 主流开源框架(三)OkHttp 源码解析 已经讲过了,这里就不展开来讲了。

我们继续看下 create() 方法:

  /*RequestFactory*/
  okhttp3.Request create(Object[] args) throws IOException {
    @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

    int argumentCount = args.length;
    if (argumentCount != handlers.length) {
      throw new IllegalArgumentException("Argument count (" + argumentCount
          + ") doesn't match expected count (" + handlers.length + ")");
    }

    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl,
        headers, contentType, hasBody, isFormEncoded, isMultipart);

    List<Object> argumentList = new ArrayList<>(argumentCount);
    for (int p = 0; p < argumentCount; p++) {
      argumentList.add(args[p]);
      handlers[p].apply(requestBuilder, args[p]);
    }

    return requestBuilder.get()
        .tag(Invocation.class, new Invocation(method, argumentList))
        .build();
  }

可以看到,这里的 httpMethod(http 方法)、baseUrl、relativeUrl(相对路劲)、headers(请求头)等常量就是前面第(3)步中通过解析网络请求接口注解得到的,通过这些常量就可以构建一个 Request 了。

  • (2):这里的 call 是 okhttp3.Call 类型的,所以 execute() 就是 OkHttp 中的方法了,所以底层实际还是通过 OkHttp 进行网络请求。这个在讲 Android 主流开源框架(三)OkHttp 源码解析 已经讲过了,这里就不展开来讲了。

接下来就是解析响应回来的数据了,点击 parseResponse() 方法进去看看:

  /*OkHttpCall*/
  Response<T> 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);
    }

    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
      // (2.1)
      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;
    }
  }

前面那些状态码的检查等就不多说了,我们直接看下关注点(2.1),是的,这里就是通过我们设置的 Converter 将响应的数据解析成相应的实体类返回的。我们看下 convert() 方法:

  /*GsonResponseBodyConverter*/
  @Override public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      T result = adapter.read(jsonReader);
      if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
        throw new JsonIOException("JSON document was not fully consumed.");
      }
      return result;
    } finally {
      value.close();
    }
  }

很简单,就是通过 Gson 将响应数据解析成相应的实体类返回。

2.4.2 异步请求

call.enqueue(new Callback<PostmanGetBean>() {
    @Override
    public void onResponse(Call<PostmanGetBean> call, Response<PostmanGetBean> response) {
    }

    @Override
    public void onFailure(Call<PostmanGetBean> call, Throwable t) {

    }
});

点击 enqueue() 方法进去看看:

    /*ExecutorCallAdapterFactory-ExecutorCallbackCall*/
    @Override public void enqueue(final Callback<T> callback) {
      checkNotNull(callback, "callback == null");
      //(1)
      delegate.enqueue(new Callback<T>() {
        //(2)
        @Override public void onResponse(Call<T> call, final Response<T> response) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              if (delegate.isCanceled()) {
                // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
                callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
              } else {
                callback.onResponse(ExecutorCallbackCall.this, response);
              }
            }
          });
        }
        //(3)
        @Override public void onFailure(Call<T> call, final Throwable t) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              callback.onFailure(ExecutorCallbackCall.this, t);
            }
          });
        }
      });
    }
  • (1):这里的 delegate 实际为 OkHttpCall,点击这里的 enqueue() 方法进去看看:
  /*OkHttpCall*/
  @Override public void enqueue(final Callback<T> callback) {
    checkNotNull(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 {
          //(1.1)
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          throwIfFatal(t);
          failure = creationFailure = t;
        }
      }
    }

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

    if (canceled) {
      call.cancel();
    }
    //(1.2)
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
          //(1.3)
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          throwIfFatal(e);
          callFailure(e);
          return;
        }

        try {
          //(1.4)
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }

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

      private void callFailure(Throwable e) {
        try {
          //(1.5)
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    });
  }
  • (1.1):这里的 createRawCall() 方法与同步请求一样,不再展开来讲。
  • (1.2):与同步请求一样,这里的 call 是 okhttp3.Call 类型的,所以 enqueue() 就是 OkHttp 中的方法了,所以底层实际还是通过 OkHttp 进行网络请求。这个在讲 Android 主流开源框架(三)OkHttp 源码解析 已经讲过了,这里就不展开来讲了。
  • (1.3):这里的 parseResponse() 方法与同步请求一样,不再展开来讲。
  • (1.4):网络请求成功,回调到关注点(2)。
  • (1.5):网络请求失败,回调到关注点(3)。
  • (2):将 onResponse() 回调到 UI 线程,然后再回调到我们例子中的 onResponse() 方法。
  • (3):将 onFailure() 回调到 UI 线程,然后再回调到我们例子中的 onFailure() 方法。

小结:
这一步的同步与异步请求其实底层分别调用的是 OkHttp 的同步与异步请求方法,然后通过我们设置的数据转换器(Converter)将响应的数据解析成相应的实体类返回。同步请求会直接返回,异步请求则会切换到主线程再进行返回。

三、总结

  • (1)创建网络请求接口:
    这一步主要是配置 HTTP 请求需要的各种参数,例如 HTTP 请求方法、请求头、请求参数、请求地址等。
  • (2)创建 Retrofit 的实例:
    这一步是通过建造者模式来创建 Retrofit 的实例,创建的过程中主要配置了网络请求器工厂(callFactory)、网络请求的 url 地址(baseUrl)、数据转换器工厂的集合(converterFactories)、网络请求适配器工厂的集合(callAdapterFactories)、回调方法执行器(callbackExecutor)。这里使用建造者模式的好处是不需要知道 Retrofit 内部是怎么创建的,只需要传入对应的配置即可创建非常复杂的对象。
  • (3)创建网络请求接口的实例,并调用接口中的方法获取 Call 对象:
    这一步是通过动态代理模式来创建网络请求接口的实例,然后再调用网络请求接口中的方法来执行动态代理中的 invoke() 方法。这一步的作用主要是解析网络请求接口中方法上的注解、参数、获取返回类型等,也就是将网络请求接口中的方法解析成 HTTP 请求需要的各种参数。这里使用动态代理的好处是可以将网络请求接口的所有方法的调用都会集中转发到 InvocationHandler 接口的 invoke() 方法中,方便集中进行处理。
  • (4)进行网络请求:
    这一步的同步与异步请求其实底层分别调用的是 OkHttp 的同步与异步请求方法,然后通过我们设置的数据转换器(Converter)将响应的数据解析成相应的实体类返回。同步请求会直接返回,异步请求则会切换到主线程再进行返回。

参考资料:

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

推荐阅读更多精彩内容