Retrofit 学习第三弹—源码分析篇

Retrofit 是目前作为网络请求的主流框架,使用起来很方便,仅需在接口中定义方法,打上注解,而且和 Rxjava 配合起来,可以更好的完成网络请求时的线程切换问题。那么这样一个框架,我们有必要对它的源码分析一下,希望能够从中吸取到一定的东西,Retrofit 框架里面用到了很设多计模式,像外观模式,代理模式,工厂模式,适配器模式,装饰者模式等等,这无疑是学习设计模式很好的一个实例。

如果对 Retrofit 的使用还不是很熟练的话,可以看看我之前写的两篇文章:

Retrofit2.0 学习第一弹——准备篇

Retrofit2.0 学习第二弹——使用篇

在看源码之前,先来看几个问题,带着问题去看源码,这样更有目的性,不至于陷于源码的细节而不能自拔。

(1) 定义网络请求是一个接口,里面的方法都是抽象方法,这些抽象方法是怎么被执行的?

(2) 方法上的注解是如何被解析,转化成网络请求的?

(3) 网络请求我们一般采用异步请求,那么请求结果回来时,是如何进行线程切换的,即切换到主线程?

Retrofit 实例构建

构建 Retrofit 实例是通过 Retrofit.Builder 来构建的,采用构建者模式,配置参数更清晰,而且能够链式调用

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();

接下来就来看下 Builder 中有哪些参数

public static final class Builder {

    // 平台类型对象:Android
    private final Platform platform;
    
    // 网络请求的工厂,默认为 okhttp3 中的 OkHttpClient,OkHttpClient 实现了 okhttp3.Call.Factory
    private @Nullable okhttp3.Call.Factory callFactory;
    
    // 网络请求的url地址,如 www.baidu.com
    private HttpUrl baseUrl;
    
    // 数据转换器工厂的集合
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    
    // 网络请求适配器工厂的集合
    private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
    
    // 回调方法执行器
    private @Nullable Executor callbackExecutor;
    
    // 是否校验接口(针对 java8,java8 中接口方法有默认实现)
    private boolean validateEagerly;

    Builder(Platform platform) {
      this.platform = platform;
    }

    public Builder() {
      this(Platform.get());
    }

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

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

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        // 默认是 Android平台 中的主线程调度器
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // 网络请求适配器工厂的集合
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      // 数据转换器工厂的集合
      List<Converter.Factory> converterFactories =
          new ArrayList<>(1 + this.converterFactories.size());
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      // 创建 Retrofit 实例
      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }
  }

使用 Builder 构建 Retrofit 可以设置这几个参数:

参数 描述 说明
Platform 平台类型 支持 Android,IOS,Java8
okhttp3.Call 用来网络请求 Call 对象,默认为 OkHttpClient 最终生成的是 okhttp3 中的 RealCall 对象,Retrofit 中的 OkHttpCall 是对 okhttp3.Call 的一个包装
HttpUrl 网络请求的url地址 设置的基地址,如 www.baidu.com,接口中注解的是 path
Converter 数据转换器 用于转化数据,将 responseBody 转化为对象,同也可以对请求完成转化
CallAdapter 网络请求适配器 可以对 okhttp3.Call 进行包装适配,如适配使用 Rxjava 请求
Executor 请求结果调度执行器 对网络请求结果进行请求,默认是 Android 平台的 MainThreadExecutor,用来切换到主线程,可进行 UI 更新操作

上面表格中是对各个参数的一个说明,有些参数可以不用设置,会有默认参数:

(1) HttpUrl 必须指定,这个不用多说

(2) okhttp3.Call.Factory 这个参数可以不用设置,默认是 OkHttpClient,OkHttpClient 是 okhttp3 中的,它实现了 okhttp3.Call.Factory 这个接口,当然一般情况下,我们在使用时会自己设置一个 OkHttpClient,对它设置一些 log 打印,证书验证,添加 header 等

(3) Converter 使用来进行数据转化的,一般使用 GsonConverterFactory,默认是 BuiltInConverters

(4) CallAdapter.Factory 是用来生产 CallAdapter 对象的,CallAdapter 是对 okhttp3.Call 进行包装,包装后主要用于控制线程的切换操作,默认由平台提供 platform.defaultCallAdapterFactory(callbackExecutor)

(5) Executor 是请求结果调度执行器,用来切换到主线程,可进行 UI 更新操作,对于 Android 平台,默认提供的是 MainThreadExecutor,默认通过 platform.defaultCallbackExecutor() 获取

Builder 中完成这些参数的设置,就可以创建 Retrofit 的实例了,对于 Retrofit 的参数也就是 Builder 中的参数,这就是构建者模式完成的任务。有些参数可能不是很好理解,后面在流程中会慢慢引入。

构建 Call

动态代理


public interface MyService {

    @GET("v1/user")
    Call<User> getUsers();

假设有这样一个 Service 接口,想实现构建 Call实例,通过反射能够做到么?

(1) 反射是用于获取已创建实例的方法或者属性,并对其进行调用或者赋值;

(2) 反射能够加载一个类,通过 newInstance 方法创建实例,但是接口不行

基于这两条,可以得出对于接口,使用反射不能构建出 Call 实例。在 java 中还有另外一个工具,动态代理。这里对动态代理的机制做一个简单的介绍。

动态代理主要通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建接口代理实例,loader 为类加载器,interfaces 是需要生成代理对象的接口数组,handler 是用于自定义处理逻辑的对象。

Class<User> service = MyService.class;
MyInvocationHandler handler = new MyInvocationHandler();
MyService proxy = Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, handler);
Call<User> call = proxy.getUsers();
public class MyInvocationHandler implements InvocationHandler {

    public MyInvocationHandler() {
        
    }

    // proxy 是代理实例对象
    // method 这个参数表示传入接口中的所有 Method 对象
    // args => 这个参数对应当前 method 方法中的参数
    @Override
    public void invoke(Object proxy, Method method, Object[] args) {
        ...
    }
}

这么看有些童鞋可能还是不是很理解动态代理,那么看下面的代理类:

public class MyServiceProxy implements MyService {

    private InvocationHandler handler;

    public MyServiceProxy(InvocationHandler handler) {
        
    }

    @Override
    @GET("v1/user")
    public Call<User> getUsers(){
        try {
            Method method = com.xx.proxy.MyService.class.getMethod("getUsers");
            this.handler.invoke(this, method, null);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

这样看来,代理类是这样的一个类,是不是就能理解了动态代理的机制了。动态代理就是创建这样一个类,然后通过类加载器加载到内存,然后再通过反射创建出代理实例,obj 就是 MyServiceProxy 的一个实例。

Class clazz = classLoader.loadClass("com.xx.proxy.MyServiceProxy");
        Constructor constructor = clazz.getConstructor(InvocationHandler.class);
        Object obj = constructor.newInstance(handler);

Retrofit 中的动态代理

好,现在回到 Retrofit 中,看看 Retrofit 是如何使用动态代理创建 Call 对象的。

在 Retrofit 实例构建中,我们能够拿到 Retrofit 对象 retrofit,通过这个对象调用 create 方法创建 MyServiceProxy 实例,即 retrofit.create(MyService.class)。

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

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            // 方法是 Object 的一个方法,则直接执行
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            // 如果是 java8 中接口的默认实现,则直接执行
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            // 重点是这三行,也是网络请求执行的代码
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.adapt(okHttpCall);
          }
        });
  }

我们先不看 InvocationHandler 中的执行逻辑,很简单,就是利用 Proxy.newProxyInstance() 方法创建一个 MyServiceProxy 实例,有了 service 对象,就可以调用接口中的方法,来得到 Call 实例:

service.getUsers() ---> handler.invoke()

最终 handler.invoke() 返回的结果就是 Call 实例。

小结

(1)到这里,已经有了 MyServiceProxy 接口实例,就可以调用接口中的方法,我们第一个问题就有了答案,是如何完成接口中方法调用的,实际上是通过动态代理,帮助我们生成一个代理类,代理类实现了我们定义的网络请求接口,动态代理生成一个实例,通过这个实例来调用接口中的方法。

(2)Retrofit 的 create 方法这里用到了,代理模式(动态代理)和外观模式,外观模式是给出一个接口,外界通过这个接口来得到想要的结果,不需要关心其内部各个子系统实现的细节,当然,外观模式不是必要的。但是有了外观模式,对于 Retrofit 的使用者使用起来更加方便和简洁。

invoke 方法具体逻辑

//(1)创建 ServiceMethod 
ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
//(2)创建 OkHttpCall      
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
//(3)包装或者适配 okHttpCall
serviceMethod.adapt(okHttpCall);
1 创建 ServiceMethod

对于我们定义的网络接口中的每个方法,都对应一个 ServiceMethod 对象,源码中采用了缓存机制,以 method 为 key,ServiceMethod 对象为 value,缓存起来,这样能够提高效率,一个接口第二次调用时,不必重新创建 ServiceMethod 对象。

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

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = new ServiceMethod.Builder<>(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

接着看 ServiceMethod 对象首次通过 Builder 模式创建过程。

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

    public ServiceMethod build() {
      // (1) 创建 callAdapter
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      if (responseType == Response.class || responseType == okhttp3.Response.class) {
        throw methodError("'"
            + Utils.getRawType(responseType).getName()
            + "' is not a valid response body type. Did you mean ResponseBody?");
      }
      // (2) 创建 Converter
      responseConverter = createResponseConverter();

      // (3) 解析方法注解
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
      // 注意事项一
      if (httpMethod == null) {
        throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
      }

      if (!hasBody) {
        if (isMultipart) {
          throw methodError(
              "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
        }
        if (isFormEncoded) {
          throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
              + "request body (e.g., @POST).");
        }
      }
      // (4) 生成每个参数注解的解析辅助类 ParameterHandler,一个 ParameterHandler 可能对应一个或者多个注解
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
        Type parameterType = parameterTypes[p];
        if (Utils.hasUnresolvableType(parameterType)) {
          throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
              parameterType);
        }

        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
        if (parameterAnnotations == null) {
          throw parameterError(p, "No Retrofit annotation found.");
        }

        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }

      // 注意事项二
      // 方法注解和参数注解解析后的错误项,这里便于我们更好的使用 Retrofit
      if (relativeUrl == null && !gotUrl) {
        throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
      }
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError("Non-body HTTP method cannot contain @Body.");
      }
      if (isFormEncoded && !gotField) {
        throw methodError("Form-encoded method must contain at least one @Field.");
      }
      if (isMultipart && !gotPart) {
        throw methodError("Multipart method must contain at least one @Part.");
      }

      return new ServiceMethod<>(this);
    }

(1) 创建 callAdapter,实际上是通过 retrofit 实例,最终调用 nextCallAdapter() ,查找在创建 Retrofit 对象时,callAdapterFactories 集合中的 CallAdapter,默认是 platform.defaultCallAdapterFactory() 得到的 new ExecutorCallbackCall<>(callbackExecutor, call),负责将原本 call 回调转发至UI线程。

(2) 创建 Converter,同样类似,最终调用 nextResponseBodyConverter 方法,查找创建 Retrofit 对象时,converterFactories 集合中的 Converter.Factory,来拿到responseConverter对象,寻找的依据主要看该 converter 能否处理你编写方法的返回值类型,默认为 BuiltInConverters,仅仅支持返回值的实际类型为 ResponseBody 和 Void,也就说明了默认情况下,是不支持 Call < User > 这类类型的,所以一般我们使用 GsonResponseBodyConverter。

(3) 方法注解解析,通过 parseMethodAnnotation() 方法来完成,得到网络请求的类型以及请求的参数,例如 @GET("v1/user"),中相对路径 v1/user,

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);
        if (!Void.class.equals(responseType)) {
          throw methodError("HEAD method must use Void as response type.");
        }
      } 
      
      ...
}

在解析方法注解后有一些注意事项,这些是我们在使用 Retrofit 时可能出现的一些问题

  // 注意事项一
  // 没有在方法上注解网络请求类型
  if (httpMethod == null) {
    throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
  }
  // 没有请求体 body,说明不是 POST 请求,那么不应该出现 @Multipart 注解和 @FormUrlEncoded 表单类型注解,
  // 因为这两个注解用于 POST 请求
  if (!hasBody) {
    if (isMultipart) {
      throw methodError(
          "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
    }
    if (isFormEncoded) {
      throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
          + "request body (e.g., @POST).");
    }
  }

(4) 生成每个参数注解的解析辅助类 ParameterHandler,一个 ParameterHandler 可能对应一个或者多个注解,在生成 Request 时会使用到。方法参数注解有多个,在上一篇文章 Retrofit2.0 学习第二弹——使用篇 中有提到。

Retrofit注解类型.png

同样,在参数注解解析后,也有注意事项,这些注意事项就是使用时的规则,规则是按照 http 请求的规则来的。

      // 注意事项二
      // 方法注解和参数注解解析后的错误项,这里便于我们更好的使用 Retrofit
      
      // relativeUrl 是在 方法注解中获取 @GET("v1/user"),
      // 如果方法注解中没有标注,那么在参数注解中应该打上 @Url 注解,指明地址的相对路径
      if (relativeUrl == null && !gotUrl) {
        throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
      }
      // 不是 POST 请求,去打上了 @Body 注解,会报错
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError("Non-body HTTP method cannot contain @Body.");
      }
      // 方法注解上有 @FormUrlEncoded 注解,参数中需要有 @Field 注解或者 @FieldMap 注解
      if (isFormEncoded && !gotField) {
        throw methodError("Form-encoded method must contain at least one @Field.");
      }
      // 方法注解上有 @Multipart 注解,参数中需要有 @Part 注解或者 @PartMap 注解
      if (isMultipart && !gotPart) {
        throw methodError("Multipart method must contain at least one @Part.");
      }

经过一系列的校验之后,没有抛出错误,就可以创建一个 ServiceMethod 对象。

2 创建 OkHttpCall
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
  OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) {
    this.serviceMethod = serviceMethod;
    this.args = args;
  }

这一步没什么说的,就是创建 OkHttpCall 对象而已。

3 适配 okHttpCall

serviceMethod.adapt(okHttpCall) 调用 ServiceMethod 中的 adapt() 方法,还记得在创建 ServiceMethod 时获取的 callAdapter 么,在平台类型 Android 中提供的 ExecutorCallAdapterFactory.get() 方法生成的 CallAdapter 对象,再通过 CallAdapter 对象 adapt() 方法得到 ExecutorCallbackCall 对象,这样 Call 请求对象的构建过程就完成了。


  final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
  final Executor callbackExecutor;

  ExecutorCallAdapterFactory(Executor callbackExecutor) {
    this.callbackExecutor = callbackExecutor;
  }

  @Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public Call<Object> adapt(Call<Object> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }

  static final class ExecutorCallbackCall<T> implements Call<T> {
    final Executor callbackExecutor;
    final Call<T> delegate;

    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
    }
    
    ...
    
  }
}

ExecutorCallbackCall 是对 Executor 的一个装饰,采用的是装饰者模式,在请求回调时通过 MainThreadExecutor 切换到 UI 线程,切换过程中先通过 Call 等待回调, run 方法执行时,也有判断。

这个 Call 实际上是步骤 2 中创建的 OkHttpCall 对象,在 ExecutorCallbackCall 中是一个静态代理对象,通过 OkHttpCall 执行同步请求方法 execute() 或者 异步方法 enqueue(final Callback< T > callback)。

小结:这一部分主要在拿到 service 代理对象后,调用接口方法获取 Call 对象。过程中最重要就是 ServiceMethod,具体的工作都交给它来完成,ServiceMethod 对象进行网络请求参数配置:通过解析网络请求接口方法的参数、返回值和注解类型,从 Retrofit 中获取对应的网络请求的 url 地址、网络请求执行器、网络请求适配器、数据转换器,最后通过调用 callAdapter.adapt 方法得到一个 Call 对象,适配的目的就是对 Call 请求回调进行操作,如果是异步请求,将异步线程切换到 UI 线程。

调用 Call 执行请求

在上一步 Call 对象构建过程中,最终得到的是一个 ExecutorCallbackCall 对象,那么就看看 ExecutorCallbackCall 是如何执行网络请求的。


  static final class ExecutorCallbackCall<T> implements Call<T> {
    final Executor callbackExecutor;
    final Call<T> delegate;

    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
    }

    @Override public void enqueue(final Callback<T> callback) {
      checkNotNull(callback, "callback == null");

      delegate.enqueue(new Callback<T>() {
        @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);
              }
            }
          });
        }

        @Override public void onFailure(Call<T> call, final Throwable t) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              callback.onFailure(ExecutorCallbackCall.this, t);
            }
          });
        }
      });
    }

    @Override public boolean isExecuted() {
      return delegate.isExecuted();
    }

    @Override public Response<T> execute() throws IOException {
      return delegate.execute();
    }

    @Override public void cancel() {
      delegate.cancel();
    }

    @Override public boolean isCanceled() {
      return delegate.isCanceled();
    }

    @SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone.
    @Override public Call<T> clone() {
      return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone());
    }

    @Override public Request request() {
      return delegate.request();
    }
  }

ExecutorCallbackCall 内部有 execute 和 enqueue 方法,即同步和异步执行方法,在创建 ExecutorCallbackCall 对象过程中,我们知道 ExecutorCallbackCall 实际上是通过 OkHttpCall 这个代理来完成请求的,我们看下 OkHttpCall 的异步请求 enqueue 方法。

@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 {
          // 创建 call 对象
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          throwIfFatal(t);
          failure = creationFailure = t;
        }
      }
    }

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

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

        try {
          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 {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    });
  }

首先是构建 Call 对象,这个 Call 不是我们步骤 2 中的 Call 对象,而是 okhttp3.Call 对象,真正完成网络请求的对象,我们构建的 Call 是对它的一个外层包装,okhttp3.Call 的创建需要 一个 Request 参数,这个应该有 ServiceMethod 来完成,因为 ServiceMethod 对象中包含网络请求的全部参数,构建最终调用 ServiceMethod 的 toCall 方法。


okhttp3.Call toCall(@Nullable Object... args) throws IOException {
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
        contentType, hasBody, isFormEncoded, isMultipart);

    @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

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

    for (int p = 0; p < argumentCount; p++) {
      handlers[p].apply(requestBuilder, args[p]);
    }

    return callFactory.newCall(requestBuilder.build());
  }

ServiceMethod 对象中的参数就是在上面 retrofit.loadServiceMethod(method) 创建过程中赋值的,包括解析方法注解,方法参数注解,最终构建出 Request 对象,callFactory 实际上是 OkHttpClient。

到这里就可以回答第二个问题【方法上的注解是如何被解析,转化成网络请求的?】,就是在 serviceMethod 创建过程中完成的,解析方法注解,方法参数注解解析等,然后在 toCall 方法中进行拼接完成,当然不是简单地拼接,具体过程这里就不再分析了。

有了 okhttp3.Call 对象,接着就可以调用 call.enqueue() 方法执行异步请求。请求的具体过程这里不展开,具体过程是调用了 okhttp3.Call 执行的,后面会单独写一篇 okhttp3 的文章。请求结果如果顺利,如何解析?
通过调用 OkHttpCall # response = parseResponse(rawResponse),无需看代码应该也能够脑补出来, 如果你还记得在构建 OkHttpCall 对象时,将 ServiceMethod 对象作为参数传递进去,所以 parseResponse 最后调用的是 ServiceMethod 中的 Conver 来进行数据解析工作的。

最后还剩下一个问题,回调后如何切换线程到 UI 线程?回头看下 ExecutorCallbackCall 中的 enqueue 方法,callbackExecutor 实际是 MainThreadExecutor,callbackExecutor 的 execute方法最后是通过主线程的 Handler 切换到 UI 线程中

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);
              }
    }
    
    
  static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

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

到这里,Retrofit 的请求过程基本就分析完了,其请求的主要过程:

(1) 请求过程实际由代理 OkHttpCall 发起,需要构建 okhttp3.Call 对象

(2) 构建 okhttp3.Call 对象需要参数 Request,Request 对象构建参数在 ServiceMethod 对象中在创建时就已经准备好,serviceMethod.toCall() 方法就是利用这些参数完成 Request 构建,生成 okhttp3.Call 对象。

(3) okhttp3.Call 请求结果回调时,对数据的解析最终还是通过 serviceMethod.toResponse() 来完成解析的。

(4) 异步请求是在子线程,Retrofit 默认是通过 MainThreadExecutor 的来切换到 UI 线程。

总结

Retrofit 整个源码分析也就差不多了,整体流程基本覆盖到,那么最后再总结一下:

1、首先构造 Retrofit,主要参数,baseurl、callFactory(默认okhttpclient)、converterFactories、adapterFactories,excallbackExecutor

2、通过 create() 方法拿到接口的实现代理类,这里利用动态代理来实现

3、在 invoke 方法内部,拿到方法注解以及方法参数,方法参数注解等,构造 ServiceMethod,ServiceMethod 完成注解解析,构建 Request 和 数据转换等大量工作。最后 calladapter 对 Call 进行装饰返回。

4、拿到 Call 就可以执行 enqueue 或者 execute 方法了。

以上分析并不是基于 Retrofit 与 Rxjava 结合的分析,关于 Rxjava 部分的分析,有兴趣的童鞋可以自行分析下,重点也是集中在对 Call 的包装和线程切换部分。

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

推荐阅读更多精彩内容