retrofit应用详解与源码解析--源码解析

本文深入解析Retrofit的源码,从Retrofit的创建、服务创建、请求命令创建到请求执行的过程。讲解了Builder的创建、设置URL、数据转换器、Call的创建等步骤,并涉及Platform、ConverterFactories、CallAdapterFactories等关键组件的作用。通过源码分析,揭示了Retrofit如何通过注解驱动实现网络请求的全过程。
摘要由CSDN通过智能技术生成

本文出自门心叼龙的博客,属于原创类容,未经允许,不得转载。

本专栏的同步视频教程已经发布到CSDN学院:https://edu.csdn.net/course/detail/30408

上一篇文章我们通过12个小案例,给大家演示了retrofit的各种用法,retrofit如果从用的角度上来讲,他玩儿的就是个注解,通过接口方法表示一个请求,通过方法注解,参数注解来封装请求的参数。同样是对OkHttp的封装,我们在okhttp专题也同样对其封装过,一种是集中式封装,一种是链接封装,但是retrofit用着更加简单,通过简单的注解配置就能表示一个网络请求,另外请求适配器和响应转换器的自定义使他的扩展性更强,在加上RxJava的配合使用,就更加强大了,还有与kotlin协程的新玩法,简直就是帅呆了,这个后面再讲,今天这篇文章主要是给大家分享retrofit源码解析。我们不但要会用,还要知道它底层的工作原理。读书破万卷下笔如有神,阅读源码也是同样的道理:读码千万行,下键如有神。


特别提示,本专题是基于最新版:retrofit 2.9.0研究的,我们还是以用户登录的接口为例:

public void loginGet(View view) {
    	Log.v("MYTAG","loginGet start...");
        //1.利用建造者模式创建一个Retrofit
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://192.168.0.189:8080")
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        //2.通过动态代理创建一个用户服务
        UserService userService = retrofit.create(UserService.class);

        //3.创建一个登录命令
        Call<BaseResponse> loginCall = userService.loginGet("mxdl", "123456");

        //4.执行登录命令
        loginCall.enqueue(new Callback<BaseResponse>() {
            @Override
            public void onResponse(Call<BaseResponse> call, Response<BaseResponse> response) {
                Log.v("MYTAG", "onResponse start...");
                Log.v("MYTAG", response.body().toString());
            }

            @Override
            public void onFailure(Call<BaseResponse> call, Throwable t) {
                Log.v("MYTAG", "onFail start...");
                Log.v("MYTAG", t.toString());
            }
        });

    }

整个过程分为四步,首先通过建造者模式创建了一个Retrofit,无处不在的建造者模式,JakeWhorton大神已经把建造者模式用的登峰造极了,接着调动retrofit的create方法创建了一个用户服务UserService,有了服务就可以调用它的具体的方法来创建一个请求命令,有了命令就可以执行请求了,这就是一个网络请求的使用流程。

1.Retrofit的创建

我们先看第一步retrofit的创建,我们可以第一步再次进行拆分成四个小的步骤,即如下图所示:

接下来我们看看这四小步的内部实现,它背后都做了哪些操作。

1.Builder的创建

我们先看第一步,实例化了一个Retrofit的建造者Builder,Buidler是Retrofit的静态内部类,建造者模式在OkHttp的使用也是无处不在,它的主要作用就是将一个复杂对象的创建与表示相分离,使用相同的构建过程创建出不同的对象表示。

在看Builder声明在Retrofit的内部,这是建造者模式一般通用的一个套路,下图是Retrofit类的注释,这是官方权威定义。

Retrofit它能适配一个Java接口为Http调用命令,通过在定义的方法上添加注解来定义怎样去发出一个Request请求。通过建造者创建一个实例,把你的接口传递给他的create方法去生成一个它的实现。注释的最下方是该类的作者信息,Bob Lee和JakeWharton,原来这是JakeWharton大神的真迹,是否有一种要收藏该代码的冲动。这类代码量也就600多行,下面这是Retrofit类的一个大纲视图:

Retrofit相当就是一个统帅,管理者整个家庭里面的大小事务,他有一个内部类Builder,一个核心方法create和五个非常重要的成员变量,这五个变量相对于就是三国里的五虎上将,他们各自都身怀绝技,完成不同的使命。

public final class Retrofit {
  //请求方法缓存容器
  private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
  //请求执行器
  final okhttp3.Call.Factory callFactory;
  //请求地址
  final HttpUrl baseUrl;
  //请求响应转换器
  final List<Converter.Factory> converterFactories;
  //请求适配器
  final List<CallAdapter.Factory> callAdapterFactories;
  //线程切换器
  final @Nullable Executor callbackExecutor;
  //是否立即校验
  final boolean validateEagerly;

五虎上将:

  • serviceMethodCache:请求存储器,缓存我们的请求方法

  • callFactory:请求执行器,也就是我们okhttp,retrofit是基于okhttp的,执行网络请求

  • converterFactories:数据转换器,包括请求参数的转换和响应结果的转换

  • callAdapterFactories:请求适配器,默认是Call,通过他可以适配成我们想要的结果,例如适配为RxJava的Observeable等

  • callbackExecutor:线程切换器,将子线程切换到UI主线程,以便网络请求之后更新UI

这五虎上将在Retrofit大元帅的统一领导下,他们各司其职,协同工作,他们有一个共同的小目标:完成一个网络请求,对Retrofit类有了大致的了解之后,我在回过头看Builder类的构造方法:

  public static final class Builder {
    ...
    //注释1    
    Builder(Platform platform) {
      this.platform = platform;
    }

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

Builder无参的构造器会调用注释1处的有参构造器,我们注意了构造器默认就初始化了一个参数Platform,这到底是什么鬼?从字面意思上讲是平台的意思,我看看它的Platform的get方法:

class Platform {
  private static final Platform PLATFORM = findPlatform();
  //注释1	
  static Platform get() {
    return PLATFORM;
  }
  //注释1:	
  private static Platform findPlatform() {
    return "Dalvik".equals(System.getProperty("java.vm.name"))
        ? new Android() //
        : new Platform(true);
  }

注释1处的get方法返回就是就是Platform类的静态成员变量:PLATFORM,而它是通过findPlatform方法初始化的,我们直接看注释2处的findPlatform方法,这是一个三目运算表达式,如果是android虚拟机Dalvik则返回Android对象,否则返回Platform对象,也就是说Retrofit它支持两大平台,不只是Android平台中能用,Java平台也可以使用,下面是Platform的大纲视图:

Android是Platform的子类,从下面的那些default开头的方法我们也能看出来,Platform类是给Retrofit大元帅的五虎上将提供默认实现的,如:线程切换器,请求适配器,数据转换器,需要我们注意的是它的defaultCallAdapterFactories方法和defaultCallbackExecutor方法:

class Platform {
    ...
    List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
          @Nullable Executor callbackExecutor) {
        DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor);
        return hasJava8Types
            ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
            : singletonList(executorFactory);
    }

    @Nullable
    Executor defaultCallbackExecutor() {
        return null;
    }    
    ...
}   

defaultCallAdapterFactories提供了默认的数据转换器,如果没有特别指定的话,默认就会使用Platform给我们提供的默认转换器DefaultCallAdapterFactory,本例的登录接口最终接口方法适配的时候就是用到它,defaultCallbackExecutor方法Platform默认实现是空实现返回了个null,它的子类Android对这个方法进行了复写,实现如下:

 static final class Android extends Platform {
    ...    
    @Override
    public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }
	...
    static final class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override
      public void execute(Runnable r) {
        handler.post(r);
      }
    }
    @IgnoreJRERequirement // Only called on API 26+.
  	@Nullable
  	Object invokeDefaultMethod(Method method, Class<?> declaringClass, Object object, Object... args)
      throws Throwable {
    	Lookup lookup =
        lookupConstructor != null
            ? lookupConstructor.newInstance(declaringClass, -1 /* trusted */)
            : MethodHandles.lookup();
    	return lookup.unreflectSpecial(method, declaringClass).bindTo(object).invokeWithArguments(args);
  	}
  }

返回的就是线程切换器MainThreadExecutor,它继承了Executor,内部封装了Handler,这样通过他就可以轻松的实现子线程到主线程的切换。直到到现在第一步的Retrofit的建造者Builder就创建好了,其实就是实例化了一个默认的平台Platform,Platform提供了默认的线程切换器,请求适配器和数据转化器。另外Platform还有一个额外的工作就是通过反射执行我们接口服务里面定义的default方法,因为从Java1.8开始接口里面可以提供默认的方法实现,如果是一个标准的网络接口方法就需要注解解析等后续操作。

2.设置url

建造者初始化完毕,接着就需要设置网络请求的地址url,注意了url的设置这是必须的,否则在构建的最后一步builer()的时候会直接强制抛异常,从而终止程序的继续向下执行。

  public static final class Builder {  
    ...  
    public Builder baseUrl(URL baseUrl) {
      Objects.requireNonNull(baseUrl, "baseUrl == null");
      return baseUrl(HttpUrl.get(baseUrl.toString()));
    }    
	public Builder baseUrl(String baseUrl) {
      Objects.requireNonNull(baseUrl, "baseUrl == null");
      return baseUrl(HttpUrl.get(baseUrl));
    }
	public Builder baseUrl(HttpUrl baseUrl) {
      Objects.requireNonNull(baseUrl, "baseUrl == null");
      List<String> pathSegments = baseUrl.pathSegments();
      if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
        throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
      }
      this.baseUrl = baseUrl;
      return this;
    }
    ...
  }     

注意了Builder给我们提供了三个重载的baseUrl方法,可以传入URL,也在传入一个String,但最终会调用参数为HttpUrl重载方法,也就是说传入的url都会被HttpUrl包装起来。

3.设置数据转换器

设置完了最基本的url,接下来就需要设置我们数据转换器GsonConverterFactory,这也是Retrofit类中官方给处示例的写法。

在这里插入图片描述

我们直接看源码:

public Builder addConverterFactory(Converter.Factory factory) {
   converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
   return this;
}

把传递进来的数据转换器交给了五虎上将之一converterFactories容器缓存起来,GsonConverterFactory它是一个工厂,它不但要负责响应体数据的解析,还要负责请求体的数据转换,所以这个工程提供了两个转换器,如下所示:

public final class GsonConverterFactory extends Converter.Factory {
 ...

  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(
      Type type, Annotation[] annotations, Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonResponseBodyConverter<>(gson, adapter);
  }

  @Override
  public Converter<?, RequestBody> requestBodyConverter(
      Type type,
      Annotation[] parameterAnnotations,
      Annotation[] methodAnnotations,
      Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonRequestBodyConverter<>(gson, adapter);
  }
  ...
}

GsonResponseBodyConverter负责响应体的数据转换,GsonRequestBodyConverter负责请求体数据转换。

4.正式创建

接下来我们看第四步,建造者通过一系列的构建之后最终会调用build方法来创建最终我们所需要的结果Retrofit。

build方法源码如下:

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

      // Make a defensive copy of the adapters and add the default Call adapter.
      //注释4
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

      // Make a defensive copy of the converters.
      //注释5
      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());
	  //注释6
      return new Retrofit(
          callFactory,
          baseUrl,
          unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories),
          callbackExecutor,
          validateEagerly);
    }

注释1处如果baseUrl为null那么直接就抛异常了,这也说明了baseUrl在Retrofit创建的时候是必须要设置的,紧接着在注释2处创建了Retrofit背后的大佬OKHttpClient,它是真正执行网络请求的哪个人。接下来的注释3,注释4,注释5都是从platform获取默认的线程切换器MainThreadExecutor,响应适配器DefaultCallAdapterFactory和数据转换器GsonConverterFactory,五虎上将准备完毕之后都交给了注释6处的Retrofit,这样一个Retrofit大元帅就创建完毕了。下面我画了一幅图来总结一下,第一步Retrofit创建的时候都做了哪些操作:

以上就是Retrofit创建的时候所做的事情,这一切都是为请求在做准备工作。

2.服务的创建

Retrofit这个大元帅创建完毕之后,接着需要用它的一个非常重要的方法create,它是真个Retrofit最最最核心的功能,就是用来创建接口服务的,这是我们调用一个网络请求的前提工作。

//2.通过动态代理创建一个用户服务
UserService userService = retrofit.create(UserService.class);

直接进入create方法,一看究竟:

//创建一个接口服务的实现代理对象
  public <T> T create(final Class<T> service) {
    Log.v("MYTAG","create start...");
    //检查传递进来的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 {
                Log.v("MYTAG","Proxy invoke start...");
                //如果该方法是一个Object的方法则直接就通过反射调用
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                //给方法参数赋值
                args = args != null ? args : emptyArgs;
                Log.v("isDefaultMethod:",platform.isDefaultMethod(method)+"");
                return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
              }
            });
  }

我们知道一般情况下,声明一个接口之后,需要创建接口的实现类才能使用接口的方法,但是Retrofit大元帅非常牛逼,它打破常规思维,并没有那么干,而是使用Java的一点奇技淫巧,通过Java动态代理的特性来完成了对接口实例的创建,在创建代理对象之前会对接口的合法性进行检查,具体的逻辑如下:

private void validateServiceInterface(Class<?> service) {
    //如果不是接口直接抛异常
    if (!service.isInterface()) {
      throw new IllegalArgumentException("API declarations must be interfaces.");
    }
    Deque<Class<?>> check = new ArrayDeque<>(1);
    check.add(service);
    while (!check.isEmpty()) {
      Class<?> candidate = check.removeFirst();
      //如果接口有泛型参数则直接抛异常
      if (candidate.getTypeParameters().length != 0) {
        StringBuilder message =
            new StringBuilder("Type parameters are unsupported on ").append(candidate.getName());
        if (candidate != service) {
          message.append(" which is an interface of ").append(service.getName());
        }
        throw new IllegalArgumentException(message.toString());
      }
      Collections.addAll(check, candidate.getInterfaces());
    }
    //立即校验服务方法的有效性
    if (validateEagerly) {
      Platform platform = Platform.get();
      //获取接口里面的所有方法进行有效性验证
      for (Method method : service.getDeclaredMethods()) {
        //对非默认方法和非静态方法才进行解析
        if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
          loadServiceMethod(method);
        }
      }
    }
  }

接口服务,及其方法的合法性检查如下:

  1. 如果不是接口直接抛异常
  2. 如果接口有泛型参数则直接抛异常
  3. 对非默认方法和非静态方法才进行解析

对于第三步要注意,如果需要预解析则提前会解析我们所定义的请求方法,是通过布尔变量validateEagerly来控制的,默认为false

对接口服务合法性检查完毕过后会通过动态代理生成服务代理对象,具体代码实现为:

Proxy.newProxyInstance(service.getClassLoader(),new Class<?>[] {service},new InvocationHandler() {
      @Override
      public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)throws Throwable {
                Log.v("MYTAG","Proxy invoke start...");     
      }
});

Proxy.newProxyInstance方法这是java给我们提供的,需要传递三个参数,参数1是字节码加载对象,参数2是字节码数组,参数3 InvocationHandler是具体方法在调用时的回调,接口的所有的方法的调用都会走它的invoke方法,关于动态代理的内部实现这块我们不做多讲,这属于Java范畴,在这儿我们不在扩展。这样通过Proxy.newProxyInstance一阵骚操作之后就创建了接口服务对象UserService。

3.创建请求命令

代码分析到这一步,有人可能会问题,Retrofit五虎上将其中四大将都创建了,为何serviceMethodCache一直没有创建?你问对了,这一步就是完成这个工作的, 有了接口服务UserService,我们就可以调用它里面的具体请求方法了,注意了接口方法调用完毕,并没有进行网络请求,而是返回一个网络请求命令Call,有了Call才能进行请求。

//3.创建一个登录命令
Call<BaseResponse> loginCall = userService.loginGet("mxdl", "123456");

这一步的主要工作有两个:

  1. 对方法进行解析
  2. 对方法进行适配

方法的解析会生成一个ServiceMethod,方法适配会返回我们的请求命令Call, 当loginGet方法调用时就会走创建动态代理对象时所传入的InvocationHandler对象的invoke方法,InvocationHandler的具体实现如下:

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 {
                Log.v("MYTAG","Proxy invoke start...");
                //注释1:如果该方法是一个Object的方法则直接就通过反射调用
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                //给方法参数赋值
                args = args != null ? args : emptyArgs;
                Log.v("isDefaultMethod:",platform.isDefaultMethod(method)+"");
                //注释2 
                return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
              }
}

首先会判断调用的是不是接口服务对象的超类Object所提供的方法,如:toString,equals,wait,notify等,如果是则直接通过反射调用就返回了,否则继续往下走,在注释2处会判断该方法是不是一个默认方法,java8之后允许接口里面通过定义default关键字定义一个有实现逻辑的方法,如果是默认方法则直接走platform平台提供的invokeDefaultMethod调用并直接返回,否则的话就解析我们所定义的标准网络接口方法。

代码分析到这了,就到了Retrofit中最关键的两个步骤,解析方法和适配方法,这也是整个Retrofit框架源码解析逻辑最为复杂的两个步骤。解析是对方法注解的解析落地对象为ServiceMethod,最终会缓存到Retrofit的五虎上将之一serviceMethodCache,他是一个map,具体是由loadServiceMethod操刀完成的。适配是将该方法的落地对象ServiceMethod适配为接口方法的返回对象Call,它具体是由ServiceMethod对象invoke方法来完成的,本质上最终的适配是由我们前面所讲到的Platform所提供的DefaultCallAdapterFactory来操作的,具体细节我们看具体的源码实现。

1.解析方法
//对接口方法进行解析
ServiceMethod<?> loadServiceMethod(Method method) {
  Log.v("MYTAG","loadServiceMethod start...");
  //从缓存中获取该方法
  ServiceMethod<?> result = serviceMethodCache.get(method);
  //如果有则直接就返回了
  if (result != null) return result;

  synchronized (serviceMethodCache) {
    result = serviceMethodCache.get(method);
    //如果缓存中没有则进行解析
    if (result == null) {
      //注释1 接口方法解析
      result = ServiceMethod.parseAnnotations(this, method);
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}

首先会从缓冲中获取,如果缓存有则直接就返回了,否则就会调用注释1处的ServiceMethod.parseAnnotations方法进行解析,解析完毕进行缓存。我们看ServiceMethod.parseAnnotations方法的具体解析实现:

abstract class ServiceMethod<T> {
  ...
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    //注释1
    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.");
    }
	//注释2	
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }
  ...
}  

首先会调用注释1处的RequestFactory.parseAnnotations方法,具体的方法的解析就由这一步来完成的,我们看它的具体实现

具体注解的解析数据就是有RequestFactory对象来存储的httpMethod就是请求的方法类型如get,post等,请求的path由relaviceUrl保存,最重要的是我们的参数都由parameterHandlers来存储,它是个数组,也就是说参数的最终落地对象为ParameterHandler:

以上就是ParameterHandler的大纲视图,他是一个抽象类,它有十几个这样的内部实现类,Path,Fidle,Part等等…,看到这里我们恍然大悟了,所有的注解最终都会被解析为相对应的ParameterHandler对象,这也的确藏的够深的。有了大致的认识之后,我们看RequestFactory.parseAnnotations具体的代码实现:

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

很简单,只有一行代码,有来了一个建造者,我们看具体的build()方法实现:

RequestFactory build() {
  //注释1 解析方法的注解,
  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation);
  }
  //如果该方法是非get,post等方法则直接抛异常
  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];
  //注释2 参数的解析
  for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
    parameterHandlers[p] =
        parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
  }
  //如果没有得到url则抛异常
  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);
}
//请求类型的解析
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;
  }
}

具体的处理逻辑如下:

  1. 解析方法的注解
  2. 如果该方法是非get,post等方法则直接抛异常
  3. 如果该方法是个文件上传的表单,但是没有请求体则抛异常
  4. 如果该方法是个表单,但是没有请求体则抛异常
  5. 参数的解析
  6. 如果没有得到url则抛异常
  7. 请求类型的解析

其中核心步骤为第一步和第五步,即注释1处的方法注解的解析和注释2处的参数注解的解析,具体解析逻辑的代码我们就不分析了,我们只要明白最终解析结果会被RequestFactory这个对象保存起来就行了。

方法解析完毕之后生成了一个RequestFactory对象,我们继续往下走就进入了HttpServiceMethod.parseAnnotations方法:

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

也就是说最终解析的成果RequestFactory会就交给HttpServiceMethod的parseAnnotations方法去处理,HttpServiceMethod类大纲如下:

它是一个抽象类,他有三个具体的实现类:CallAdapted,SuspendForResponse,SuspendForBody,其中后两个是对kotlin协程支持,显然我们这块会返回CallAdapted对象。这几个类他们之间的相互关系如下所示:

在这里插入图片描述

具体逻辑我们看代码它的具体实现:

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();
  }
  //注释1	
  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.");
  }
  //注释2	
  Converter<ResponseBody, ResponseT> responseConverter =
      createResponseConverter(retrofit, method, responseType);
  //注释3	
  okhttp3.Call.Factory callFactory = retrofit.callFactory;
  //注释4 
  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);
  }
}

我们直接看注释4处的关键步骤:

return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);

也就是说在这块,我们前面所做的所有成果都交给了CallAdapted对象,构建它的时候需要传递四个参数:

  1. requestFactory:RequestFactory,请求需要的参数
  2. callFactory:OkhttpClient,请求执行器
  3. responseConverter:GsonConverterFactory,数据转换
  4. callAdapter:DefaultCallAdapterFactory,请求适配

其中callFactory, responseConverter, callAdapter三大将时候Retrofit提供的,详见注释1,注释2,注释3,而CallAdapted就是我们五大将之一的ServiceMethod,创建完毕会交给交给Retrofit的serviceMethodCache缓存起来。截止目前五大将已经全部创建完毕。

2.方法适配

第一步方法解析是完成了,接下来我们看第二步方法适配,把它要适配成我们的接口方法返回对象Call。

接下来会调用CallAdapted的invoke方法进行适配,参数args就是调用loginGet方法所传入的具体的实参。

@Override
final @Nullable ReturnT invoke(Object[] args) {
  Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
  return adapt(call, args);
}

各大将和参数都交给了OkHttpCall对象,OkHttpCall就是被背后执行网络请求的那个大佬,它终于现身了,这块会直接调用CallAdapted的adapt方法:

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.adapt方法的内部又会调用callAdapter.adapt方法,callAdapter就是我们Platform所提供的DefaultCallAdapterFactory,接下来我们看DefaultCallAdapterFactory的adapt方法实现:

@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;
    }
	//注释1	
    @Override
    public Call<Object> adapt(Call<Object> call) {
      return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
    }
  };
}

即会调用注释1处的adapt方法,这里传进来的call就是OkHttpCall,显然executor不会为null,那就返回了ExecutorCallbackCall对象,同时把OkHttpCall和executor交给了他,executor就是我们的五大将之一线程切换器,ExecutorCallbackCall就是我们最终要的适配器对象,有了它我们就可以执行一个网络请求命令了。

4.请求的执行

看来ExecutorCallbackCall才是背后大佬的大佬的。它封装了OkHttpCall和callbackExecutor,callbackExecutor就是我们的MainThreadExecutor,他是用来切换线程的。

此时就到了我们请求的最后一步,一切准备就绪,发射:

//4.执行登录命令
loginCall.enqueue(new Callback<BaseResponse>() {
     @Override
     public void onResponse(Call<BaseResponse> call, Response<BaseResponse> response) {
         Log.v("MYTAG", "onResponse start...");
         Log.v("MYTAG", response.body().toString());
    }

    @Override
    public void onFailure(Call<BaseResponse> call, Throwable t) {
        Log.v("MYTAG", "onFail start...");
        Log.v("MYTAG", t.toString());
    }
 });

loginCall就是我们的ExecutorCallbackCall,我们直接看他的enqueue方法:

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;
  }
  //注释1
  @Override
  public void enqueue(final Callback<T> callback) {
    Objects.requireNonNull(callback, "callback == null");
      //注释2 执行OkHttpCall的enqueue
	  delegate.enqueue(
        new Callback<T>() {
          @Override
          public void onResponse(Call<T> call, final Response<T> response) {
            //用线程切换器切换线程  
            callbackExecutor.execute(
                () -> {
                  if (delegate.isCanceled()) {
                    // Emulate OkHttp's behavior of throwing/delivering an IOException on
                    // cancellation.
                    //如果取消则直接调用失败的方法  
                    callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
                  } else {
                    //回调onResponse方法  
                    callback.onResponse(ExecutorCallbackCall.this, response);
                  }
                });
          }

          @Override
          public void onFailure(Call<T> call, final Throwable t) {
            //回调onFailure    
            callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
          }
        });
  }

我们直接看注释2处OkHttpCall的enqueue方法的实现:

@Override
public void enqueue(final Callback<T> callback) {
  Objects.requireNonNull(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
        call = rawCall = createRawCall();
      } catch (Throwable t) {
        throwIfFatal(t);
        failure = creationFailure = t;
      }
    }
  }

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

  if (canceled) {
    call.cancel();
  }
  //注释2
  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) {
            throwIfFatal(e);
            callFailure(e);
            return;
          }

          try {
            callback.onResponse(OkHttpCall.this, response);
          } catch (Throwable t) {
            throwIfFatal(t);
            t.printStackTrace(); // TODO this is not great
          }
        }

        @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) {
            throwIfFatal(t);
            t.printStackTrace(); // TODO this is not great
          }
        }
      });
}

不管怎么样,最后都会由OkHttp的Call去完成最终的请求操作详见注释2,程序走到这之后就是OkHttp的事了,这块的逻辑在OkHttp专栏我们已经分析过了,感兴趣的可以去查阅我之前的专栏。

1.Call的创建

现在我们主要看注释1处的createRawCall方法,它创建了OkHttp的Call,有了它就可以发起一个网络请求了,createRawCall方法的具体实现如下:

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

看到了吗?你让我找的好苦啊,经历了这么多的沟沟坎坎,才看到了我们OkHttp中熟悉的方法:newCall,而requestFactory.create(args)方法不用说返回就是OkHttp中的Request。

final class 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);

  if (isKotlinSuspendFunction) {
    // The Continuation is the last parameter and the handlers array contains null at that index.
    argumentCount--;
  }

  List<Object> argumentList = new ArrayList<>(argumentCount);
  //注释1  
  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();
 }
...
}

我们直接看注释1处,会循环我们参数handlers给requestBuilder赋值,handlers就是前面我们在方法解析的时候生成的ParameterHandler,而我们登陆接口的username和password都是Query类型的,我们直接看他的apply方法:

static final class Query<T> extends ParameterHandler<T> {
  private final String name;
  private final Converter<T, String> valueConverter;
  private final boolean encoded;

  Query(String name, Converter<T, String> valueConverter, boolean encoded) {
    this.name = Objects.requireNonNull(name, "name == null");
    this.valueConverter = valueConverter;
    this.encoded = encoded;
  }

  @Override
  void apply(RequestBuilder builder, @Nullable T value) throws IOException {
    if (value == null) return; // Skip null values.

    String queryValue = valueConverter.convert(value);
    if (queryValue == null) return; // Skip converted but null values
	//注释1
    builder.addQueryParam(name, queryValue, encoded);
  }
}

看到了吗?注释1处的builder.addQueryParam方法,熟悉配方,熟悉的味道,这就是OkHttp中给url参数赋值的操作。Request被RequestFactory创建好之后,在调用OkHttp的newCall方法,就真正的创建了一个OkHttp的Call对象,有了它就可以发送一个真实的网络请求了,之后的操作流程就进入OkHttp了,关于这部分的源码分析,如果感兴趣可以看我之前所写的OkHttp专栏。

5.小结

Retrofit他就是一个大元帅,统管了五大将,他们各司其职,相互配合,只为一个目标:发起一个网络请求,他们各自在自己平凡的岗位上默默的贡献者自己的力量,姓氏排名不分先后…

  • Retrofit:大元帅
  • OkHttpClient:请求执行器
  • Converter:数据转换器
  • CallAdapter:请求适配器
  • CallBackExecutor:线程切换器
  • ServiceMethod:方法处理器
  • RequestFactory:方法解析器
  • ParameterHandler:参数处理器
  • ExecutorCallbackCall:背后的大佬
  • OkHttpCall:背后大佬的大佬

6.学习交流

  • 我的公众号:
  • Android、Java开发技术交流群
    QQ群:810970432
    email:geduo_83@163.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

门心叼龙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值