美文网首页
Retrofit 学习第三弹—源码分析篇

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

作者: Kip_Salens | 来源:发表于2019-06-10 17:37 被阅读0次

    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 的包装和线程切换部分。

    相关文章

      网友评论

          本文标题:Retrofit 学习第三弹—源码分析篇

          本文链接:https://www.haomeiwen.com/subject/oylzxctx.html