kotlin 协程学习笔记(二)Retrofit的协程原理

2022-01-24  本文已影响0人  bridegg

源码解读基于2.9.0
本笔记记录本人阅读源码思路,并非源码解读
不想看阅读源码思路的直接拉倒最下面看伪代码

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 {
                // If the method is a method from Object then defer to normal invocation.
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                args = args != null ? args : emptyArgs;
                return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
              }
            });
  }

该代码块分为两部分,validateServiceInterface,2Proxy.newProxyInstance
先看 validateServiceInterface 干了什么 通过 level记录方法调用层级

validateServiceInterface level1

  private void validateServiceInterface(Class<?> service) {
    //判断是否是接口,否则抛出异常
    if (!service.isInterface()) {
      throw new IllegalArgumentException("API declarations must be interfaces.");
    }
  //不知道干啥的,百度后是双队列,头尾均可出队列,压队列 队列个数为1  为了Collections.addAll的入参
    Deque<Class<?>> check = new ArrayDeque<>(1);
//压入
    check.add(service);
//判断不为空   
   while (!check.isEmpty()) {
      Class<?> candidate = check.removeFirst();
//判断参数>0
      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里
      Collections.addAll(check, candidate.getInterfaces());
    }
//上面都是判断逻辑 Collections存了东西

//暂时不知道这是个啥,留着
    if (validateEagerly) {
//不知道这又是啥,留着
      Platform platform = Platform.get();
//遍历传入的service,
      for (Method method : service.getDeclaredMethods()) {
//判断了一坨,
        if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
//不知道干了啥
          loadServiceMethod(method);
        }
      }
    }
  }

看完代码以后,遗留几个问题,Collections干了啥,validateEagerly是啥,Platform是啥,loadServiceMethod干了啥, Collections和Platform是其他类,大体看下这两个是干啥的
然后就尴尬的发现,这些方法都是API方法,恶补。。
百度一大坨最后得出代码结论
Collections.addAll(check, candidate.getInterfaces()); 将传入的service中放方法全部插入集合,结合while (!check.isEmpty())可以遍历所有方法
platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())是判断是否是系统或者api方法,所以不是的话必定会执行loadServiceMethod

validateEagerly是本类变量loadServiceMethod是本类方法,再看这两个

一个一个解决,

validateServiceInterface level1-> validateEagerly level2

本类搜代码,搜到了一坨变量赋值,可能有用的是这个方法,翻译下


    /**
     * When calling {@link #create} on the resulting {@link Retrofit} instance, eagerly validate the
     * configuration of all methods in the supplied interface.
     */
//机翻 在生成的改造实例上调用create时,急切地验证所提供接口中所有方法的配置。

    public Builder validateEagerly(boolean validateEagerly) {
      this.validateEagerly = validateEagerly;
      return this;
    }

意思貌似是验证方法配置,没啥用,估计默认都验证
最后看 loadServiceMethod 这里明显返回了一个类型,而且这个方法还有一个地方调用,在 Proxy.newProxyInstance的invoke中,先看代码

validateServiceInterface level1-> loadServiceMethod level2

  ServiceMethod<?> loadServiceMethod(Method method) {

//缓存里有就直接返回
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;
//加锁判断缓存里有没有
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
//干了一坨事,扔到缓存里
        result = ServiceMethod.parseAnnotations(this, method);
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

需要追下ServiceMethod.parseAnnotations 不知道往缓存里塞了啥,但是可以知道在validateServiceInterface中执行的目的就是将ServiceMethod.parseAnnotations的方法扔到缓存里,提供给 Proxy.newProxyInstance的invoke使用,所以下面直接快进到Proxy.newProxyInstance中的loadServiceMethod,继续追下去

Proxy.newProxyInstance level1-> loadServiceMethod level2->ServiceMethod.parseAnnotations level3

 static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
//这是啥?
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
//这又是啥?貌似拿了方法的返回类型
    Type returnType = method.getGenericReturnType();
//返回api未知类型,抛异常
    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.");
    }
//又返回了啥?
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

ServiceMethod.parseAnnotations level3 有两个貌似看起来不简单的类,RequestFactory ,HttpServiceMethod深入追下

ServiceMethod.parseAnnotations level3-> RequestFactory.parseAnnotations level4

 static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
//建造者
    return new Builder(retrofit, method).build();
  }

//这坨应该是处理那一堆注解的建造者
  Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();
      this.parameterTypes = method.getGenericParameterTypes();
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }

继续往下看

 RequestFactory build() {
      for (Annotation annotation : methodAnnotations) {
//猜测是处理get那一坨注解
        parseMethodAnnotation(annotation);
      }
此处省略一坨抛出异常的判断
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
//猜测处理参数那一坨注解
        parameterHandlers[p] =
            parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
      }

此处省略一坨抛出异常的判断


      return new RequestFactory(this);
    }

关键方法就三个,parseMethodAnnotation,parameterHandlers,new RequestFactory

ServiceMethod.parseAnnotations level3-> RequestFactory.parseAnnotations level4->parseMethodAnnotation level 5

    private void parseMethodAnnotation(Annotation annotation) {
//一堆ifelse处理标注的注解,并拿到值
//请求方式的注解都执行parseHttpMethodAndPath,并把注解里的值传入
      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注解里的 
//  String method();
  /**
   * A relative or absolute path, or full URL of the endpoint. This value is optional if the first
   * parameter of the method is annotated with {@link Url @Url}.
   *
   * <p>See {@linkplain retrofit2.Retrofit.Builder#baseUrl(HttpUrl) base URL} for details of how
   * this is resolved against a base URL to create the full endpoint URL.
   */
//  String path() default "";

 // boolean hasBody() default false;
//三个参数传入
        HTTP http = (HTTP) annotation;
        parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
      } else if (annotation instanceof retrofit2.http.Headers) {
//header处理
        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;
      }
    }
这里将各种请求类型的值赋值给全局变量
 private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
//将代码判断省略后的代码
      this.httpMethod = httpMethod;
      this.hasBody = hasBody;
      this.relativeUrl = value;
      this.relativeUrlParamNames = parsePathParameters(value);
    }


所以parseMethodAnnotation这个代码就是将写的接口上放的所有注解转换成Builder里的成员变量,
RequestFactory.parseAnnotations得到Builder就能拿到 请求方式,请求url等数据
回头继续看

//结合上下代码查看,可以看到这个是
// Builder(Retrofit retrofit, Method method)里的
// this.parameterAnnotationsArray = method.getParameterAnnotations();方法赋值
//method.getParameterAnnotations是获取方法参数前的注解
//所以这里是获取参数所有注解
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
        parameterHandlers[p] =
            parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
      }

因为代码太多,大体和方法注解一样,这里拿到参数注解去判断是否是Field ,HeaderMap等注解,然后返回一个ParameterHandler.Header而ParameterHandler最终还是向HttpUrl.Builder urlBuilder里塞了入参,这个就不纠结了,毕竟只是为了找协程。。。
但是!!!
在parseParameter里发现了一段代码

    private @Nullable ParameterHandler<?> parseParameter(
        int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
      ParameterHandler<?> result = null;
      if (annotations != null) {
        for (Annotation annotation : annotations) {
          ParameterHandler<?> annotationAction =
              parseParameterAnnotation(p, parameterType, annotations, annotation);
//省略判断代码
          result = annotationAction;
        }
      }

      if (result == null) {
        if (allowContinuation) {
          try {
            //判断参数类型是否是Continuation.class 设置一个isKotlinSuspendFunction
            if (Utils.getRawType(parameterType) == Continuation.class) {
              isKotlinSuspendFunction = true;
              return null;
            }
          } catch (NoClassDefFoundError ignored) {
          }
        }
        throw parameterError(method, p, "No Retrofit annotation found.");
      }

      return result;
    }

isKotlinSuspendFunction 从名字上来看,应该是判断suspend关键字方法 suspend和Continuation.class什么关系???
经过一番搜索,终于得知,suspend在编译成字节码后 会变成一个Continuation.class的入参,验证方法很简单,用as提供的tools->kotlin->showKotlinByteCode 查看简单的一个例子

    @GET("/1211-1")
    suspend fun getEnglishWordsByLaunch(
        @Query("count") count: Int?,
    ): BaseKey<DataKey<EnglishKey>>

字节码文件

  public abstract getEnglishWordsByLaunch(Ljava/lang/Integer;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
  @Lretrofit2/http/GET;(value="/1211-1")
  @Lorg/jetbrains/annotations/Nullable;() // invisible
    // annotable parameter count: 2 (visible)
    @Lretrofit2/http/Query;(value="count") // parameter 0
    // annotable parameter count: 2 (invisible)
    @Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 0
    @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 1
    LOCALVARIABLE this Lcom/demo/kotlinapp/net/Urls; L0 L1 0
    LOCALVARIABLE count Ljava/lang/Integer; L0 L1 1

所以经过上面的代码分析回到build方法中

 RequestFactory build() {
//省略处理方法体的注解
//省略处理方法体判断
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
//处理所有入参标注,如果最后一个是参数Continuation.class类型,将builder中的isKotlinSuspendFunction设置成true
        parameterHandlers[p] =
            parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
      }

//省略其他判断
      return new RequestFactory(this);
    }


再回到 parseAnnotations

ServiceMethod.parseAnnotations level3-> RequestFactory.parseAnnotations level4

 static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
//包含各种各样的参数,URL,请求方式,请求体,isKotlinSuspendFunction
   RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

//省略一坨判断
//这里一定处理了什么
   return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
 }

ServiceMethod.parseAnnotations level3->HttpServiceMethod.parseAnnotations level4

这一大坨代码,看着头疼 一点点啃吧

  static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
//是否是协程方法
    boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
//是否需要返回Response类型??
    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]);
//如果返回类型是Response.class 就是等待返回结果,并且拿到responseType
      if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
        responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
        continuationWantsResponse = true;
      } else {
//其他类型需要自己判断是否为空。。。。
      }
//通过responseType创建了一个带泛型的Call (相当于返回了一个Call<T>)
      adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
//这里点进去看了看就是会给注解加一个SkipCallbackExecutor 类型的注解,主要是为了不回掉callback方法
      annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
    } else {
//其他的和以前差不多
      adapterType = method.getGenericReturnType();
    }

    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    Type responseType = callAdapter.responseType();
//省略一坨判断异常
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);
//开始创建call了
    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if (!isKotlinSuspendFunction) {
//不是kotlin方法创建普通CallAdapted
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } else if (continuationWantsResponse) {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
//协程返回Response的CallAdapted
      return (HttpServiceMethod<ResponseT, ReturnT>)
          new SuspendForResponse<>(
              requestFactory,
              callFactory,
              responseConverter,
              (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
    } else {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
//协程返回Bod的CallAdapted
      return (HttpServiceMethod<ResponseT, ReturnT>)
          new SuspendForBody<>(
              requestFactory,
              callFactory,
              responseConverter,
              (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
              continuationBodyNullable);
    }
  }

看来关键方法是在SuspendForResponse和SuspendForBody了,而且Proxy.newProxyInstance level中也执行了这两个返回对象的invoke方法,所以先从SuspendForResponse的invoke入手

Proxy.newProxyInstance level1-> loadServiceMethod level2-> invoke level3

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

而且 SuspendForResponse 中没有重写 invoke方法 所以关键逻辑在SuspendForResponse的adapt中

@Override
    protected Object adapt(Call<ResponseT> call, Object[] args) {
      call = callAdapter.adapt(call);

      //noinspection unchecked Checked by reflection inside RequestFactory.
      Continuation<Response<ResponseT>> continuation =
          (Continuation<Response<ResponseT>>) args[args.length - 1];

      // See SuspendForBody for explanation about this try/catch.
      try {
        return KotlinExtensions.awaitResponse(call, continuation);
      } catch (Exception e) {
        return KotlinExtensions.suspendAndThrow(e, continuation);
      }
    }

可以看到关键代码在awaitResponse中

suspend fun <T> Call<T>.awaitResponse(): Response<T> {
  return suspendCancellableCoroutine { continuation ->
    continuation.invokeOnCancellation {
      cancel()
    }
    enqueue(object : Callback<T> {
      override fun onResponse(call: Call<T>, response: Response<T>) {
        continuation.resume(response)
      }

      override fun onFailure(call: Call<T>, t: Throwable) {
        continuation.resumeWithException(t)
      }
    })
  }
}

看到enqueue和suspendCancellableCoroutine恍然大悟
suspendCancellableCoroutine 可以将回调参数转换为协程 用法就是 用resume返回结果
enqueue也清晰了,是call的扩展函数,直接执行的call里的enqueue
java和kotlin的参数不一样是因为编译造成的,kotlin的方法编译后会生成类似以下代码

  public static awaitResponse(Lretrofit2/Call;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;

所以相当于传入的call执行了enqueue方法,并通过suspendCancellableCoroutine方法转化为协程方法回执到结果中
好的,到此这整个流程就清晰了
最终执行顺序伪代码如下

->Retrofit.Builder()...build().create(你写的请求接口类) //开始准备执行请求
->validateServiceInterface{//做接口类参数验证
     ->loadServiceMethod(){//通过缓存获取 ServiceMethod
          if(缓存中没有该方法的ServiceMethod){
              ->ServiceMethod.parseAnnotations{
                //获取ServiceMethod

                  ->RequestFactory.parseAnnotations(){
                    //获取是否是kotlin方法isKotlinSuspendFunction
                    //(suspend 关键字会编译成Continuation,通过最后一个入参判断)
                    //获取方法上的注解和入参注解,并做验证    
                  }
                 ->HttpServiceMethod.parseAnnotations(){
                    //通过  isKotlinSuspendFunction 创建 CallAdapted或者SuspendForResponse/SuspendForBody 并返回
                }
              }
          }else{
          //直接返回 缓存中ServiceMethod  ServiceMethod =CallAdapted或者SuspendForResponse/SuspendForBody
          }
      
      }
}

->注册你写的请求接口类的动态代理
->等待你执行方法
->你执行某个接口请求method
->触发动态代理 invoke 
-> loadServiceMethod获取到ServiceMethod(CallAdapted/SuspendForResponse/SuspendForBody)
->ServiceMethod.invoke
        ->new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
        ->adapt{
            if CallAdapted   返回 CallAdapter<ResponseT, ReturnT>  需要手动调用enqueue
            if SuspendForResponse {
              ->KotlinExtensions.awaitResponse(){
                    通过java入参call 转化成改参数的扩展函数,直接执行enqueue
                    通过suspendCancellableCoroutine转化协程方法 直接返回Callback接口结果
              }
          }
           if SuspendForBody {
              ->KotlinExtensions.awaitNullable/await(){
                    通过java入参call 转化成改参数的扩展函数,直接执行enqueue

                    通过suspendCancellableCoroutine转化协程方法 直接返回Callback接口结果
              }
          }
      }
->得到invoke结果,若接口为suspend修饰,直接返回SuspendForResponse或SuspendForBody返回的接口结果,否则返回ExecutorCallbackCall接口(详见DefaultCallAdapterFactory)

以上就是通过源码理解Retrofit的协程原理

上一篇 下一篇

猜你喜欢

热点阅读