【网络编程】从Retrofit原理来看HTTP

create()

让我们先深入到create 里,

public <T> T create(final Class<T> service) {
  validateServiceInterface(service);
  ....
}

validateServiceInterface()

我们发现首先会执行validateServiceInterface(),名字意思为验证服务接口,传入的参数也是接口所对应的类文件。

	private void validateServiceInterface(Class<?> service) {
	    // 1. 检查传入的参数是否为接口
	    if (!service.isInterface()) {
	        throw new IllegalArgumentException("API declarations must be interfaces.");
	    }
	
	    // 2. 使用双端队列来存储待检查的接口
	    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());
	    }
	
	    // 3. 如果设置了validateEagerly标志为true,则进行进一步的验证
	    if (validateEagerly) {
	        Platform platform = Platform.get();
	        // 遍历接口中声明的方法
	        for (Method method : service.getDeclaredMethods()) {
	            // 检查方法是否为默认方法且不是静态方法
	            if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
	                // 加载服务方法
	                loadServiceMethod(method);
	            }
	        }
	    }
	}

我们可以分三部分看看他是如何验证接口的:

  • 第一部分简单判断这个类引用是否为接口(!service.isInterface()),否则抛出异常.
  • 第二部分,这里是先创建了一个双向队列(ArrayDeque),然后并将这个service添加到队列中。然后进入一个循环,直到队列为空。在每次循环中,从队列的开头取出一个service用于检查。如果发现有一个元素含泛型(candidate.getTypeParameters().length != 0),则抛出异常.提示"Type parameters are unsupported on …",其中包含相应类的名称。如果取出来的service不是初始的service类,错误消息还会指示它是service的接口之一。最后,将candidate的所有接口添加到队列中,以便进一步检查它们是否有类型参数。
  • 第三部分我们看到了一个validateEagerly的标志位(激进验证),如果标志位validateEagerly为真,即需要进行全面验证,则获取当前平台(Platform)的实例。然后,遍历service类声明的所有方法。对于不是默认方法(default method)且不是静态方法的每个方法(Retrofit不支持或不认为是),调用loadServiceMethod方法进行加载,遍历方法进行加载,就算验证的一部分了。
  • 在我们使用Retrofit时,我们建立的xxxService接口里的方法在第一次加载时会进行初始化操作,在create()中,就会进行,这样更早的快速暴露问题,但是在初始化的过程中也会进行一些反射的内容,很耗时。

动态代理

接下来让我们看create()剩下部分的实现

	public <T> T create(final Class<T> service) {
	    // 验证服务接口的有效性
	    validateServiceInterface(service);
	
	    return (T) Proxy.newProxyInstance(
	        service.getClassLoader(),
	        new Class<?>[] { service },
	        new InvocationHandler() {
	            private final Platform platform = Platform.get();
	            private final Object[] emptyArgs = new Object[0];
	
	            @Override
	            public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
	                throws Throwable {
	                // 如果方法是 Object 类的方法,则执行普通的调用
	                if (method.getDeclaringClass() == Object.class) {
	                    return method.invoke(this, args);
	                }
	                
	                args = args != null ? args : emptyArgs;
	                
	                // 如果方法是默认方法(Java 8 及以上版本),则调用默认方法实现
	                return platform.isDefaultMethod(method)
	                    ? platform.invokeDefaultMethod(method, service, proxy, args)
	                    : loadServiceMethod(method).invoke(args);
	            }
	        }
	    );
	}

可以看出来后面的内容只是一个方法的调用:newProxyInstance() 动态代理
有三个参数,

  • service.getClassLoader() 简单获取的类加载器
  • new Class<?>[] { service } 动态代理的接口
  • new InvocationHandler() 后边代理实例调用方法
    所以我们只需要看真正的实现,真正干事的部分,即代理实例调用方法的部分的invoke()里,在实际操作里,会执行对应Retrofit生成的动态代理类的invocationHandler.invoke()
       	 @Override
         public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
             throws Throwable {
             // 如果方法是 Object 类的方法,则直接 执行普通的调用,不会代理
             if (method.getDeclaringClass() == Object.class) {
                 return method.invoke(this, args);
             }
             
             args = args != null ? args : emptyArgs;
             
             // 如果方法是默认方法(Java 8 及以上版本),则调用默认方法实现
             return platform.isDefaultMethod(method)
                 ? platform.invokeDefaultMethod(method, service, proxy, args)
                 : loadServiceMethod(method).invoke(args);
         }
     }

老版本Android不支持Java8,也不知道什么叫默认方法

	@IgnoreJRERequirement // Only called on API 24+.
	boolean isDefaultMethod(Method method) {
	  return hasJava8Types && method.isDefault();
	  //会检测是否有java8 对应的特性,那我直接返回false
	}

那么核心代码从invoke()走到了loadServiceMethod(method).invoke(args);
里边的args : args = args != null ? args : emptyArgs;


loadServiceMethod(method)

ServiceMethod<?> loadServiceMethod(Method method) {
  // 1首先尝试从缓存serviceMethodCache中获取已存在的 ServiceMethod 对象
  ServiceMethod<?> result = serviceMethodCache.get(method);
  if (result != null) return result;

  // 2如果缓存中不存在,则需要进行同步操作,确保线程安全
  synchronized (serviceMethodCache) {
    // 2再次尝试从缓存中获取 ServiceMethod 对象
    result = serviceMethodCache.get(method);
    if (result == null) {
      // 如果缓存中依然不存在,则需要解析注解并创建 ServiceMethod 对象
      result = ServiceMethod.parseAnnotations(this, method);
      // 将新创建的 ServiceMethod 对象添加到缓存中
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}

整个过程就是一个带缓存(serviceMethodCache)的加载,缓存中有直接返回,没有则先进行创建,再放到缓存中然后返回.

serviceMethodCache是Retrofit2的一个缓存机制(实际上是Map),用于缓存已经解析的ServiceMethod对象,ServiceMethod 对象代表了一个被注解修饰的接口方法,每一个接口方法都有对应。(毕竟解析注解很耗时)

所以此处的核心代码其实是如何创建对应的ServiceMethod,也就是:

result = ServiceMethod.parseAnnotations(this, method);

我们继续深入跟进:

abstract class ServiceMethod<T> {

  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
  
  // 第1步:解析方法的注解,创建RequestFactory对象,
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

    // 第2步:获取方法的返回类型
    Type returnType = method.getGenericReturnType();
    
    // 第3步:检查返回类型是否包含类型变量或通配符
    if (Utils.hasUnresolvableType(returnType)) {
      throw methodError(
          method,
          "方法的返回类型不能包含类型变量或通配符:%s",
          returnType);
    }
    
    // 第4步:检查返回类型是否为void
    if (returnType == void.class) {
      throw methodError(method, "服务方法不能返回void。");
    }

    // 第5步:解析HTTP服务方法特定的注解
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

  //使用给定的参数调用服务方法,并返回结果。。。
  abstract @Nullable T invoke(Object[] args);
}

我们发现:

  • 调用了RequestFactory.parseAnnotations(retrofit, method);,并保存返回值,这里是工厂模式
  • HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  • 下边的invoke()很眼熟,这不正好是前边loadServiceMethod(method).invoke(args);的吗

核心代码是第1步和第5步,该方法的目的还是获取Service接口所对应的ServiceMethod<T>

最后返回的还是HttpServiceMethod 的方法。即HttpServiceMethod.parseAnnotations()

那么既然转换注解还是靠该类实现的,那么最最重要的最终的invoke()是不是也是一个该类实例呢?

我们发现点进去后是:abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT>
这是一个继承ServiceMethod<T>的类,很有趣,似乎熟悉的又回来了,那么继承了该类,HttpServiceMethod就一定会实现invoke()这个抽象方法:

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

这里是先生成了一个OkHttpCall(),(毕竟Retrofit底层还是okhttp)并且作为参数带入了adapt()方法

  • 这⾏代码负责将 ServiceMethod 解读到的信息(主要是⼀个 RequestFactory 、⼀个 OkHttpClient 和⼀个 ResponseConverter )封装进 OkHttpCall
  • ⽽这个对象可以在需要的时候(例如它的 enqueue() ⽅法被调⽤的时候), 利⽤ RequestFactoryOkHttpClient 来创建⼀个 okhttp3.Call 对象,并调⽤这个对象来发起⽹络请求,
  • 然后利⽤ ResponseConverter 对结果进⾏预处理之后,交回给 Retrofit 的 Callback 。

在实际操作中,异步请求是实际上交给了okhttp来做,在OkHttpCall类中,这里有异步请求的底层,通过parseResponse(rawResponse)转换成对象。

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

接着来看adapt(),adapt()意思就是适配,转换。它是一个抽象方法,

那我们只能继续回看到parseAnnotations这个方法:代码很长,我此处只显示真正的核心部分

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
    ///....省略n行
  if (!isKotlinSuspendFunction) {
     //核心部分,生成HttpServiceMethod并返回
    return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    
  } else ...
}

这里new了一个HttpServiceMethod的内部类CallAdapter(),最后还是会走到:

callAdapter.adapt(call);

这个⽅法会使⽤⼀个 CallAdapter 对象来把 OkHttpCall 对象进⾏转换,⽣成⼀个新的对象。

  • 默认情况下,返回的是⼀个 ExecutorCallbackCall ,它的作⽤是把操作切回主线程后再交给 Callback 。

另外,如果有⾃定义的 CallAdapter,这⾥也可以⽣成别的类型的对象,例如 RxJava 的 Observable

  • 34
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

xoliu1

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

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

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

打赏作者

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

抵扣说明:

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

余额充值