zoukankan      html  css  js  c++  java
  • retrofit对协程支持的源码分析

    2.6.0retrofit支持了kotlinsuspend

    retrofit的代码是怎么识别一个方法时suspend函数的呢

    private suspend fun testSuspend(key: String, age: Int) {
        withContext(Dispatchers.Default) {
            delay(10000)
            1
        }
    }

    对应的java代码为

    private static final Object testSuspend(String key, int age, Continuation $completion)

    可以看到在testSuspend参数列表中增加了一个参数kotlin.coroutines.Continuation

    retrofit就是根据这个特点来判断的具体在retrofit2.RequestFactory.Builder#build

    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); //最后入参表示是否为最后一个参数
    }

    retrofit2.RequestFactory.Builder#parseParameter

    private @Nullable ParameterHandler<?> parseParameter(
        int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
      ParameterHandler<?> result = null;
      if (annotations != null) {
        // 对注解进行解析,并赋值给result,
      }
    
      if (result == null) {
        if (allowContinuation) {
          try {
            if (Utils.getRawType(parameterType) == Continuation.class) { // 如果最后一个参数时Continuation,则表示此方法时kotlin的suspend函数。
              isKotlinSuspendFunction = true;
              return null;
            }
          } catch (NoClassDefFoundError ignored) {
          }
        }
        throw parameterError(method, p, "No Retrofit annotation found.");
      }
    
      return result;
    }

    如何支持kotlin协程呢

    retrofit2.HttpServiceMethod#parseAnnotations

    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) { // 1.kotlin suspend
        Type[] parameterTypes = method.getGenericParameterTypes();
        Type responseType =
            Utils.getParameterLowerBound(
                0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
        if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) { // 2.如果接口方法的返回值是Response<XXX>的话
          // 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();
      }
    
      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.");
      }
    
      Converter<ResponseBody, ResponseT> responseConverter =
          createResponseConverter(retrofit, method, responseType);
    
      okhttp3.Call.Factory callFactory = retrofit.callFactory;
      if (!isKotlinSuspendFunction) {
        return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
      } else if (continuationWantsResponse) { // 2.如果接口方法的返回值是Response<XXX>的话
        //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
        return (HttpServiceMethod<ResponseT, ReturnT>)
            new SuspendForResponse<>(
                requestFactory,
                callFactory,
                responseConverter,
                (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
      } else { // 3.如果接口方法的返回值是不是Response<XXX>的话
        //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
        return (HttpServiceMethod<ResponseT, ReturnT>)
            new SuspendForBody<>(
                requestFactory,
                callFactory,
                responseConverter,
                (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
                continuationBodyNullable);
      }
    }

    这里看一下SuspendForBody

    static final class SuspendForBody<ResponseT> extends HttpServiceMethod<ResponseT, Object> {
      private final CallAdapter<ResponseT, Call<ResponseT>> callAdapter;
      private final boolean isNullable;
    
      SuspendForBody(
          RequestFactory requestFactory,
          okhttp3.Call.Factory callFactory,
          Converter<ResponseBody, ResponseT> responseConverter,
          CallAdapter<ResponseT, Call<ResponseT>> callAdapter,
          boolean isNullable) {
        super(requestFactory, callFactory, responseConverter);
        this.callAdapter = callAdapter;
        this.isNullable = isNullable;
      }
    
      @Override
      protected Object adapt(Call<ResponseT> call, Object[] args) {
        call = callAdapter.adapt(call);// 1.还是原call
    
        //noinspection unchecked Checked by reflection inside RequestFactory.
        Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];
    
        // Calls to OkHttp Call.enqueue() like those inside await and awaitNullable can sometimes
        // invoke the supplied callback with an exception before the invoking stack frame can return.
        // Coroutines will intercept the subsequent invocation of the Continuation and throw the
        // exception synchronously. A Java Proxy cannot throw checked exceptions without them being
        // declared on the interface method. To avoid the synchronous checked exception being wrapped
        // in an UndeclaredThrowableException, it is intercepted and supplied to a helper which will
        // force suspension to occur so that it can be instead delivered to the continuation to
        // bypass this restriction.
        try {
          return isNullable
              ? KotlinExtensions.awaitNullable(call, continuation)
              : KotlinExtensions.await(call, continuation); // 2.默认是不支持null的,所以调用这个
        } catch (Exception e) {
          return KotlinExtensions.suspendAndThrow(e, continuation);
        }
      }
    }

    adapt方法是在最后请求是调用的,具体可以看之前的retrofit源码分析

    接着看retrofit2.KotlinExtensions#await

    suspend fun <T : Any> Call<T>.await(): T {
        return suspendCancellableCoroutine { continuation ->
            continuation.invokeOnCancellation {
                cancel()
            }
            enqueue(object : Callback<T> {
                override fun onResponse(call: Call<T>, response: Response<T>) {
                    if (response.isSuccessful) {
                        val body = response.body()
                        if (body == null) {
                            val invocation = call.request().tag(Invocation::class.java)!!
                            val method = invocation.method()
                            val e = KotlinNullPointerException(
                                "Response from " +  method.declaringClass.name + '.' + method.name +
                                " was null but response body type was declared as non-null"
                            )
                            continuation.resumeWithException(e)
                        } else {
                            continuation.resume(body)
                        }
                    } else {
                        continuation.resumeWithException(HttpException(response))
                    }
                }
    
                override fun onFailure(call: Call<T>, t: Throwable) {
                    continuation.resumeWithException(t)
                }
            })
        }
    }

    可以看到这个是kotlin的代码了上边java代码调用此方法时传递的时两个参数对于kotlin来说第一个参数就是扩展函数的接收者receiver第二个参数是kotlin生成的continuation

  • 相关阅读:
    Discuz利用百度ping把我们网站自动提交到百度
    正则表达式速查表1
    thinkphp 新浪新闻采集代码演示
    php采集一网站的精美图片
    百度知道的php爬虫
    新浪新闻采集程序
    mysql pid文件丢失解决办法
    js位运算-按位非
    mysql表损坏解决方案
    h5新API之WebStorage解决页面数据通信问题
  • 原文地址:https://www.cnblogs.com/muouren/p/15762084.html
Copyright © 2011-2022 走看看