Retrofit解析

·  阅读 872
Retrofit解析

本次源码解析基于2.9.0,如有描述错误,请大佬们评论指出。

一、Retrofit的作用

Retrofit基于okhttp,简化了okhttp请求接口的操作,而且适配Rxjava和kotlin的协程,但目前还没有适配kotlin的Flow,如果要适配,自己封装也是可以的。

先看看早期直接使用okhttp请求 image.png 构造请求+解析响应+使用okhttp的线程池执行(当然okhttp也有同步调用),一堆操作很是麻烦,如果加上loading显示/隐藏、线程切换代码会更加复杂,retrofit+rxjava的经典搭配适应潮流就出现了。

retrofit适配的返回值 image.png 支持协程的话,小伙伴可能会懵逼了,注解啥的都好说,这个都好处理,它怎么拿到方法上的suspend,其实retrofit不需要拿suspend这个修饰符,因为java压根没有suspend,编译之后显真身,suspend在kotlin看来就只是一个挂起函数标志,在编译成java字节码后偷偷摸摸多了个用于回调的接口Continuation。 image.png 先来看看retrofit用法

//就是创建我们的retrofit客户端
public class HttpManager {
    private Retrofit mRetrofit;
    private Map<Class<?>, Object> mMap = new HashMap<>();
    private static class SingletonInstance {
        private static final HttpManager INSTANCE = new HttpManager();
    }
    public static HttpManager getInstance() {
        return SingletonInstance.INSTANCE;
    }
    private HttpManager() {
        mRetrofit = new Retrofit.Builder()
                .client(自定义的okhttpClient) //不写的话,retrofit也会默认创建
                .baseUrl("https://xxxx.xxxx.cn")
                .addConverterFactory(ScalarsConverterFactory.create())//转换为String对象
                .addConverterFactory(GsonConverterFactory.create())//转换为Gson对象
                //接口返回值适配
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
    }
    public <T> T create(Class<T> cls) {
        if (!mMap.containsKey(cls)) {
            T t = mRetrofit.create(cls);
            mMap.put(cls, t);
        }
        return (T) mMap.get(cls);
    }
}
复制代码

image.png image.png image.png

Q: 那个Call直接可以enqueue,那个observeable在subscribe后数据就可以接收,协程挂起恢复后就直接返回了,有点厉害,咋实现?
拿简单的observable来说,我们会使用create方法创建一个Observable,然后需要自己管理数据的发射,retrofit操作的Observable估计也要自行处理数据的发射???是这样么?后文解释。 image.png

Q: 方法上有注解,请求参数也有注解,返参还有泛型,这个怎么处理?
方法上有注解,请求参数也有注解,拿到method后解析注解,这个不难,拿到这些注解后,构建Request,如果是post的话,还要构造RequestBody,要注明MediaType,返回值是Call、Observable等决定是走默认的还是rxjava,或者协程,返回值上的泛型也很关键,在okhttp的rawResponse拿到后,要解析响应,需要预先选择合适的解析器解析数据。

二、从Retrofit+Observable请求post接口熟悉流程

  • 2.1、post请求编写

image.png 这里没有为协程专门搞个什么CallAdapterFactory哦,因为协程走默认的DefaultCallAdapterFactory。 这个默认的在创建Retrofit对象时添加进去的。 image.png image.png

  • 2.2、 retrofit的创建--->Retrofit的build方法

public Retrofit build() {
   .....
  okhttp3.Call.Factory callFactory = this.callFactory;
  if (callFactory == null) { //没有就默认可以给你创建OkhttpClient
    callFactory = new OkHttpClient();
  }
  Executor callbackExecutor = this.callbackExecutor;
  if (callbackExecutor == null) { //回调的线程池,安卓默认就是主线程切换
    callbackExecutor = new MainThreadExecutor();
  }
  //添加默认的 new DefaultCallAdapterFactory(callbackExecutor)
  List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
  callAdapterFactories.addAll(new DefaultCallAdapterFactory(callbackExecutor)
  List<Converter.Factory> converterFactories = new ArrayList<>( 1 +    this.converterFactories.size() + platform.defaultConverterFactoriesSize());
  //添加一些默认的转换器
  converterFactories.add(new BuiltInConverters());
  converterFactories.addAll(this.converterFactories);
  converterFactories.addAll(new OptionalConverterFactory());
  return new Retrofit(....);
}
//android的回调的主线程池
static final class MainThreadExecutor implements Executor {
  private final Handler handler = new Handler(Looper.getMainLooper());
  @Override
  public void execute(Runnable r) {
    handler.post(r);
  }
}
复制代码

如果没看retrofit的build方法,在debug时,发现我们明明只添加了两个转换器和一个RxjavaCallAdapter,为啥会多出来一些不认识的转换器,那是因为retrofit在创建时,偷偷摸摸给你添加了一些默认的。多贴心呐。 记住:callFactory就是OkHttpClient

  • 2.3、 经典retrofit的动态代理---->create方法

image.png create方法的返回值是我们自定义的Api接口对象,所以可以直接调用Api的方法---废话。
InvocationHandler的invoke方法的返回值是Object,Api接口类里面方法的返回值可能是Call、Observable、Object,采用Object做返回值就都可以支持了。

来看看loadServiceMethod

private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
//用一个支持并发安全的Map缓存Method了
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,他的子类有好几个。 image.png 其中以Rxjava和Call方式请求都是返回的CallAdapted.
协程返回的是SuspendForBody或者SuspendForResponse.

来看看parseAnnotations

static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
 //先创建RequestFactory,然后创建HttpServiceMethod
  RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
  ........
  返回值是Void以及不能解析的返回值类型判断
  .....
  return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
复制代码
  • 2.4、 RequestFactory创建---->解析方法注解以及方法参数

很关键的RequestFactory.parseAnnotations(...)返回RequestFactory,它里面含有太多信息

static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
  return new Builder(retrofit, method).build();
}
//方法参数处理器数组
ParameterHandler<?>[] parameterHandlers
RequestFactory build() {
  for (Annotation annotation : methodAnnotations) {
    //解析方法上的注解
    parseMethodAnnotation(annotation);
  }
 //hasBody isFormEncoded relativeUrl isFormEncoded isMultipart gotPart
//那我们的post这里肯定是有body的哈,不是表单提交,这里不是很重要的细节,不写
 ......
  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(annotation)
这里就以例子@POST("app/courses/behaviour")参考,解析方法上的注解获取请求方式以及短路径

private void parseMethodAnnotation(Annotation annotation) {
 //判断方法上注解的类型 DELETE  GET HEAD PATCH PUT OPTIONS HTTP POST retrofit2.http.Headers
 //Multipart FormUrlEncoded)
 //这里只保留POST
  if (annotation instanceof DELETE) {
   ....
  } else if (annotation instanceof POST) {
     //这里就是处理这个  @POST("app/courses/behaviour")
      this.httpMethod ="POST";
      this.hasBody = true;
      String value=((POST) annotation).value();
      if (value.isEmpty()) {
        return;
      }
      .......
      //保存短路径
      this.relativeUrl = value;
      this.relativeUrlParamNames = parsePathParameters(value);
  }
  ......
}
复制代码

来看解析方法参数--->parseParameter
先讲讲我们的Body类
fun getCourse(@Body info: CourseInfo): Observable 参数用@Body注解,设计了ParameterHandler这个类 image.png 这里拎出Body这个类,它要构造RequestBody,需选择xxxRequestBodyConverter才能构建成功。 image.png

private @Nullable ParameterHandler<?> parseParameter(....) {
  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类判断
         //这个判断有点粗糙
        if (Utils.getRawType(parameterType) == Continuation.class) {
          isKotlinSuspendFunction = true;
          return null;
        }
      } catch (NoClassDefFoundError ignored) {
      }
    }
   .....
  }
  return result;
}
复制代码

上面👆就解释了,Retrofit怎么判断是走协程的,是通过判断参数里面有没有一个Continuation类型,是的话,就走协程。下面在参数里面添加了Continuation,但是我希望他走Rxjava,但不幸的事,它认为应该走协程,那就奔溃了。后文解释。 image.png

关键的地方来了,这个parseParameterAnnotation方法有400+行,一个方法400+行呐。但我们只看需要的 这里以注解是Field和Body为例。

@Nullable
private ParameterHandler<?> parseParameterAnnotation(
    int p, Type type, Annotation[] annotations, Annotation annotation) {
 //annotation instanceof Url/Path/Query/QueryName/QueryMap/Header/HeaderMap等太多,删除
 //以Field和Body类讲解
  if (annotation instanceof Url) {
    .....
  } else if (annotation instanceof Field) {
    .....
    //省略部分逻辑
    Converter<?, String> converter = retrofit.stringConverter(type, annotations);
    return new ParameterHandler.Field<>(name, converter, encoded);
  } else if (annotation instanceof Body) {
    //body类型 肯定就不是表单类型了
    ......
    Converter<?, RequestBody> converter;
    converter = retrofit.requestBodyConverter(type, annotations, methodAnnotations);
    //requestBody转换器存在于请求参数处理器中
    return new ParameterHandler.Body<>(method, p, converter);
  }
  return null; // Not a Retrofit annotation.
}
复制代码

Field注解: image.png 来看看retrofit.stringConverter(type, annotations)方法 image.png image.png 它的type是Int类型,找不到合适的RequestBody转换器,最后都只能是强制toString。是不是所有的Int float short等数值类型,都会被retrofit搞成String呢?我看到了它有个 addFormField方法,value是统一的String类型。

void addFormField(String name, String value, boolean encoded)
复制代码

Body注解 image.png 先说下3个解析器工厂:都包含用于RequestBody构建的解析器 和 将ResponseBody转成返回值上的泛型类型的解析器两种

  • BuiltInConverters:请求参数是Okhttp3的RequestBody泛型类型 + 响应类型是Okhttp3的ResponseBody、Void或者Kotlin的Unit类型等
  • ScalarsConverterFactory:请求体的MediaType是(text/plain; charset=UTF-8)且请求体类型是基本数据类型的包装类型或者String类型 + 响应体是基本数据类型的包装类型/String类型。
  • GsonConverterFactory: 请求体的MediaType是("application/json; charset=UTF-8")且能被Gson序列化/反序列化,或者是响应体要能转换成返回值上的泛型类型。

三个转换器工厂的requestBodyConverter方法依次尝试,只要能正常返回converter,就立马返回了。

public <T> Converter<T, RequestBody> requestBodyConverter(
    Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations) {
    for (int i = 0, count = converterFactories.size(); i < count; i++) {
      Converter.Factory factory = converterFactories.get(i);
      Converter<?, RequestBody> converter =
          factory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, this);
      if (converter != null) {
        return (Converter<T, RequestBody>) converter;
      }
    }
    throw new IllegalArgumentException(....);
}
//GsonConverterFactory的requestBodyConverter
@Override
public Converter<?, RequestBody> requestBodyConverter(....) {
  TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
  return new GsonRequestBodyConverter<>(gson, adapter);
}
复制代码

我们这里的Body是数据类,肯定是GsonConverterFactory能处理的,所以它将这个GsonRequestBodyConverter保存在RequestFactory里面了。注意请求和响应的解析器可以不一样,视具体情况而定,这里接口参数是数据类,返回值是Integer类型,那响应的解析器是ScalarResponseBodyConverters.IntegerResponseBodyConverter。

  • 2.5、HttpServiceMethod的创建(HttpServiceMethod.parseAnnotations)

一个HttpServiceMethod对象需要requestFactory, callFactory, responseConverter, callAdapter。 前面还只是创建了RequestFactory,callFactory。先记住我们的接口方法样子,方便看下面的代码哈。

@POST("app/courses/behaviour")
fun getCourse(@Body info: CourseInfo): Observable<Int>
复制代码
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(...) {
  Annotation[] annotations = method.getAnnotations();
  Type adapterType;
  if (isKotlinSuspendFunction) {
    //协程获取返回值类型
    .....
  } else {
    //如果返回值是Observable<Int>,那类型就是Integer.class
    adapterType = method.getGenericReturnType();
  }
 //创建CallAdapter 区分是走默认的还是Rxjava
  CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, 
                            method, adapterType, annotations);
  Type responseType = callAdapter.responseType();
   .....//响应类型判断
  //响应类型不能是okhttp3的Response
  //响应类型是Retrofit的Response的话,必须是类似Response<String>这样带有泛型的。
  //这里的responseType是Integer类型
  ......
  //创建响应体的转换器
  Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType);
  okhttp3.Call.Factory callFactory = retrofit.callFactory;
  if (!isKotlinSuspendFunction) { 
    //是CallAdapted不是CallAdapter,一个HttpServiceMethod的子类
    return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
  } else if (continuationWantsResponse) {
     .......
     //协程相关
  }
}
//获取CallAdapter看是走DefaultCallAdapterFactory 还是RxJava2CallAdapterFactory
private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
    Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
for (int i = 0, count = callAdapterFactories.size(); i < count; i++) {
  //返回值是Observable Maybe Signle Flowable,就走RxJava2CallAdapterFactory创建RxJava2CallAdapter
  //返回值是Call就走DefaultCallAdapterFactory创建CallAdapter
  CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
  if (adapter != null) {
    return adapter;
  }
 }
}
复制代码

callFactory:就是OkHttpClient,可以自定义,也可以直接用Retrofit默认创建的。
callAdapter的判断:
接口方法上的返回值是Observable、Maybe 、Signle 、Flowable等类型,就是RxJava2CallAdapter。
接口方法上的返回值是Call类型就是CallAdapter
这里getCourse方法上是Observable那肯定就是RxJava2CallAdapter了。
responseConverter的判断:
肯定是根据响应类型来判断了,这里的返回值是Observable,泛型是Kotlin的Int类型,那就是java的Integer类型。 哪种转换器能处理,当然是ScalarsResponseBodyConverters了。 image.png

请求还没发,响应体的解析器就先准备好了,牛逼。
请求体和响应体的解析器可以任意搭配,具体看用户的需求。
image.png

  • 2.6、 BodyObservable的创建--->HttpMethodService(CallAdapted)调用invoke方法

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

先创建一个Retrofit的Call,然后调用RxJava2CallAdapter的adapt方法

@Override
public Object adapt(Call<R> call) {
 //我们当时添加到retrofit是addCallAdapterFactory(RxJava2CallAdapterFactory.create()就是非Async的
  Observable<Response<R>> responseObservable =
      isAsync ? new CallEnqueueObservable<>(call) : new CallExecuteObservable<>(call);
  Observable<?> observable;
  if (isResult) {
    ......
  } else if (isBody) {
    //一般请求都是为了寻求Body,所以必定是这个
    observable = new BodyObservable<>(responseObservable);
  } else {
    ......
  }
  //适配Flowable  Single  Maybe Completable的逻辑
  .......
  return RxJavaPlugins.onAssembly(observable);
}
复制代码

BodyObservable(CallExecuteObservable):装饰器设计模式,两个配合完成返回。CallExecuteObservable从名字上看就是进行call的同步调用获取Response,BodyObservable就是发射T,单一职责原则用的很6。
针对Rxjava2CallAdapterFactory类型,invoke方法返回的Observable就是BodyObservable
针对DefaultCallAdapterFactory类型,invoke方法返回的ExecutorCallbackCall(Retrofit的Call请求方式)或者 Retrofit的Call(Retrofit的协程请求方法)

  • 2.7、 接口请求--->BodyObservable的数据发射以及响应体的处理

1、通常我们在用retrofit+Rxjava请求接口时,都会写个subscribeOn(Schedulers.io()),那就是表示我们要在子线程处理网络请求,那就要求retrofit中应该使用okhttp的同步方法请求接口。
2、BodyObservable(CallExecuteObservable)被订阅后,就开始调用它的subscribeActual方法,在其里面发起call的同步请求(call.execute()),等待响应,解析响应体,分发observer的那几个回调方法,如有dispose及时取消请求。

final class CallExecuteObservable<T> extends Observable<Response<T>> {
  private final Call<T> originalCall;
  CallExecuteObservable(Call<T> originalCall) {
    this.originalCall = originalCall;
  }
  @Override
  protected void subscribeActual(Observer<? super Response<T>> observer) {
   //所谓的clone就是new 新的OkhttpCall
    Call<T> call = originalCall.clone(); 
    CallDisposable disposable = new CallDisposable(call);
    //处理onSubscribe的回调
    observer.onSubscribe(disposable);
    if (disposable.isDisposed()) {
      return;
    }
    boolean terminated = false;
    try {
      //这里的call是同步调用的哈
      Response<T> response = call.execute();
      if (!disposable.isDisposed()) {
      //处理observer的onNext方法
        observer.onNext(response);
      }
      if (!disposable.isDisposed()) {
        terminated = true;
        //处理observer的onComplete方法
        observer.onComplete();
      }
    } catch (Throwable t) {
      Exceptions.throwIfFatal(t);
      if (terminated) {
        RxJavaPlugins.onError(t);
      } else if (!disposable.isDisposed()) {
        try {
           //处理observer的onError方法
          observer.onError(t);
        } catch (Throwable inner) {
          Exceptions.throwIfFatal(inner);
          RxJavaPlugins.onError(new CompositeException(t, inner));
        }
      }
    }
  }
  private static final class CallDisposable implements Disposable {
    private final Call<?> call;
    private volatile boolean disposed;
    CallDisposable(Call<?> call) {
      this.call = call;
    }
    @Override
    public void dispose() {
      disposed = true;
      //取消请求
      call.cancel();
    }
    @Override
    public boolean isDisposed() {
      return disposed;
    }
  }
}
复制代码

创建Request后发起同步请求

@Override
public Response<T> execute() throws IOException {
//使用callFactory(OkhttpClient)创建okhttp的Call
  okhttp3.Call call =  callFactory.newCall(requestFactory.create(args));
  if (canceled) {
    call.cancel();
  }
  return parseResponse(call.execute());
}
//requestFactory的Create创建request
okhttp3.Request create(Object[] args) throws IOException {
 //之前把所有的参数都保存在参数处理器的数组里面
  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);
  //kotlin肯定有个参数Continuation不能用于请求体创建。
  if (isKotlinSuspendFunction) {
    // The Continuation is the last parameter and the handlers array contains null at that index.
    argumentCount--;
  }
  List<Object> argumentList = new ArrayList<>(argumentCount);
  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();
}
复制代码

Body(ParameterHandler的子类)的apply方法开始构建请求体 image.png image.png 请求url(baseUrl+relativeUrl拼接)、头字段的处理

Request.Builder get() {
  HttpUrl url;
  HttpUrl.Builder urlBuilder = this.urlBuilder;
  if (urlBuilder != null) {
    url = urlBuilder.build();
  } else {
    url = baseUrl.resolve(relativeUrl);
  }
  RequestBody body = this.body;
  ......
  MediaType contentType = this.contentType;
  if (contentType != null) {
    if (body != null) {
      body = new ContentTypeOverridingRequestBody(body, contentType);
    } else {
      headersBuilder.add("Content-Type", contentType.toString());
    }
  }
  return requestBuilder.url(url).headers(headersBuilder.build()).method(method, body);
}
复制代码

解析okhttp3的rawResponse为Retrofit的Response

 Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
   ResponseBody rawBody = rawResponse.body();
   rawResponse = rawResponse.newBuilder()
           .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
           .build();
   int code = rawResponse.code();
   if (code < 200 || code >= 300) {
     try {
       // 使用okio读取的
       ResponseBody bufferedBody = Utils.buffer(rawBody);
       return Response.error(bufferedBody, rawResponse);
     } finally {
       rawBody.close();
     }
   }
   if (code == 204 || code == 205) {
     rawBody.close();
     return Response.success(null, rawResponse);
   }
   ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
   try {
     //之前保存的响应体转换器去转换ResponseBody
     T body = responseConverter.convert(catchingBody); 
     return Response.success(body, rawResponse);
   } catch (RuntimeException e) {
     catchingBody.throwIfCaught();
     throw e;
   }
 }
复制代码

image.png

响应的处理:两个Observable搭配返回用户需要的数据 image.png image.png BodyObservable发射onNext的是reponse.body()

可以看下onNext调用顺序。 image.png

👆基本上就走完了Retrofit+Rxjava的请求接口的所有流程。

三、Retrofit+Call请求接口流程

image.png image.png 从使用上看,跟Retrofit+Rxjava相比,这个原理更简单,但是用的人少吧,它的返回值是Call,走的是默认的 DefaultCallAdapterFactory,上文提到的MainThreadExecutor就是给这种场景下切换线程用的。
Retrofit中OkhttpCall类的enqueue方法就是调用okhttp的call的enqueue方法,直接用okhttp的线程池请求接口。

//ExecutorCallbackCall就只是用来处理线程切换啊
static final class ExecutorCallbackCall<T> implements Call<T> {
  final Executor callbackExecutor; //MainThreadExecutor
  final Call<T> delegate; //Retrofit的Call
  .....
  @Override
  public void enqueue(final Callback<T> callback) {
    Objects.requireNonNull(callback, "callback == null");
   //异步的啊
    delegate.enqueue(
        new Callback<T>() {
          @Override
          public void onResponse(Call<T> call, final Response<T> response) {
            callbackExecutor.execute(
                () -> {
                  if (delegate.isCanceled()) {
                    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(() -> callback.onFailure(ExecutorCallbackCall.this, t));
          }
        });
  }
//下面是Retrofit Call的 enqueue方法  
@Override
public void enqueue(final Callback<T> callback) {
  okhttp3.Call call = createRawCall();
  .....
  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) {
            throwIfFatal(e);
            callFailure(e);
            return;
          }
          try {
            callback.onResponse(OkHttpCall.this, response);
          } catch (Throwable t) {
            throwIfFatal(t);
          }
        }
        @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);
          }
        }
      });
}
复制代码

创建请求和解析响应,跟上面rxjava的都差不多。但是它只能返回Retrofit的Response,不能返回用户想要的类型,用户还需要通过response.body()获取,有点麻烦。

四、Retrofit+kotlin协程请求接口流程

前面提到Retrofit把请求认定为走协程,是看参数里面有没有Continuation参数,一般我们在方法上加suspend的话,编译器编译之后,就会干掉suspend,然后在方法的参数的最后一个位置,加上Continuation类型的参数, 创建HttpServiceMethod先会创建RequestFactory,RequestFactory创建会去解析参数,就可以捕捉到这个Continuation。

//from RequestFactory
private @Nullable ParameterHandler<?> parseParameter(
    int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
   .....
   //正常处理参数
   .....
  if (result == null) {
    if (allowContinuation) {
      try {
        //捕捉到Continuation
        if (Utils.getRawType(parameterType) == Continuation.class) {
          //设置标记位后面会用到
          isKotlinSuspendFunction = true; 
          return null;
        }
      } catch (NoClassDefFoundError ignored) {
      }
    }
   ......
  return result;
}
复制代码

image.png Q: 为啥没有按照返回值Observable来走Rxjava的那一套?
因为RequestFactory要比CallAdapter的创建早,在创建RequestFactory时解析参数发现Continuation了,就设置了一个标记位,标识要走协程那一套。

所以我们只能按照下面的写: image.png image.png Continuation类如下: image.png

所以,在解析参数时,Continuation参数是排除在外的,它用来获取响应类型,编译器的泛型在IDE里面反编译是看不到的,但是在运行时还是可以使用反射Method来获取它的泛型类型。 image.png

Q: 在运行时还是可以使用反射Method来获取它的泛型类型,泛型不是会被擦除吗,怎么还能获取到?
先看看下面的例子: image.png 我们反编译看看,有没有能捕捉到泛型还保留的证据?
javac编译后,使用javap -verbose查看 image.png 方法体里面使用类似Map、List这样的泛型,反射是找不到的,但是在方法上或者方法参数里,是可以获取到的,因为Signature保留了对应的泛型信息

Q: 协程请求的接口方法声明上没有Call,它是怎么选择DefaultCallAdapterFactory的呢?

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
    Retrofit retrofit, Method method) {
  //先创建RequestFactory
  RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method); 
  boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;  //true
  ......
  Annotation[] annotations = method.getAnnotations();
  Type adapterType;
  if (isKotlinSuspendFunction) {
    Type[] parameterTypes = method.getGenericParameterTypes();
    //找那个Continuation<?super ExtendItem>参数,取的是ExtendItem的下界
    Type responseType = Utils.getParameterLowerBound(0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
     .....
    //获取返回值的类型,,这里偷偷摸摸加东西了,Call.class-->看来要走DefaultCallAdapterFactory
    adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
    //而且给方法注解加多一个SkipCallbackExecutor类型的注解
    annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
  } else {
    adapterType = method.getGenericReturnType();
  }
 //创建CallAdapter 区分是走默认的还是Rxjava那一套
  CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method, adapterType, annotations);
//校验响应类型是不是okhttp的Response,是的话直接throw Exception
//检验响应类型是不是retrofit的Response,是的话,没带泛型,直接throw Exception
//校验请求如果是head请求且返回值是Void类型,不满足的就直接throw Exception
  .....
  //创建转换器
  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) {
   //协程的返回值是Response<XXX>类型
    return (HttpServiceMethod<ResponseT, ReturnT>)
        new SuspendForResponse<>(requestFactory, callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
  } else {
       //协程的返回值是xxx类型-->我们这里就是ExtendItem类型,所以走这里
    return (HttpServiceMethod<ResponseT, ReturnT>)
        new SuspendForBody<>(requestFactory, callFactory, responseConverter, (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter, continuationBodyNullable);
  }
}
复制代码

requestFactory.isKotlinSuspendFunction在创建RequestFactory时,走协程的话会设置为true。
上面依次创建requestFactory、callAdapter,响应转换器responseConverter,最后创建SuspendForBody(HttpServiceMethod的子类)。--->callFactory就是OkhttpClient。
这个地方细节有点多,suspend编译之后,参数加了个Continuation,但是CallAdapter只有2种,协程走的是默认的DefaultCallAdapterFactory,而且为了不跟retrofit的Call请求方式起冲突了,偷偷摸摸的在我们代码里面下毒了。

    Type adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
    Annotation[] annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
复制代码

Utils.ParameterizedTypeImpl就是ParameterizedType的子类,这里rawType就是Call,responseType就是ExtendItem

static final class ParameterizedTypeImpl implements ParameterizedType { 
   .....
  ParameterizedTypeImpl(@Nullable Type ownerType, Type rawType, Type... typeArguments) {
    this.ownerType = ownerType;
    this.rawType = rawType;
    this.typeArguments = typeArguments.clone();
  }
  @Override
  public Type getRawType() {
    return rawType;
  }
  .......
}
复制代码

从Contiuation参数上获取到响应类型是ExtendItem类型,然后再经过封装,将响应类型的getRawType返回Call,这点很关键,这下就决定能走DefaultCallAdapterFactory了。
再来看看ensurePrensent方法:偷偷摸摸给方法加上一个新的注解SkipCallbackExecutor返回,然后给CallAdapter创建使用。贴心呐。
SkipCallbackExecutor 表示跳过线程切换到主线程,协程才不用Retrofit的主线程切换MainThreadExecutor,是用户通过协程调度器实现。 image.png

最后,我们看看suspend实际处理的样子: image.png 创建callAdapter 之前也讲过的
image.png

看看DefaultCallAdapterFactory的get方法,有用到SkipCallbackExecutor注解。 image.png 看那个getRawType(returnType),只要是Call.class就能返回非null的CallAdapter,所以Retrofit封装响应类型的rawType为Call是有用的。
同时基于SkipCallbackExecutor注解的判断,导致CallAdapter的adapt方法直接将入参的call原样返回了。

响应体的转换器准备

private final List<Converter.Factory> converterFactories = new ArrayList<>();
private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
复制代码

前面讲过的,他会尝试converFactories,我们这里是ExtendItem类型,那么它的转换器,只可能是GsonResponseBodyConverter了 image.png

Retrofit的java代码跟协程代码融合的地方-->SuspendForBody的invoke方法

final @Nullable Object invoke(Object[] args) {
  Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
  //之前SuspendForBody的CallAdapter啥事没干,入参是Retrofit中的Call,因没有处理线程切换的操作,返参还是Retrofit中的Call,看后面的图哈。
  Call<ResponseT> call = callAdapter.adapt(call, args)
  Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];
 try {
   return isNullable ? KotlinExtensions.awaitNullable(call, continuation)
                       : KotlinExtensions.await(call, continuation);
 } catch (Exception e) {
   return KotlinExtensions.suspendAndThrow(e, continuation);
 }

}
复制代码

image.png Retrofit的call创建好后,因我们的返回值是ExtendItem,不是可空的,所以就丢给KotlinExtensions.await方法,开始协程处理。

suspend fun <T : Any> Call<T>.await(): T {
 //支持可取消的协程
  return suspendCancellableCoroutine { continuation ->
    continuation.invokeOnCancellation {
      //取消接口请求
      cancel()
    }
    //走okhttp的异步接口请求
    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)
      }
    })
  }
}
复制代码

java调用转到kotlin调用,java的第一个参数是Call,这里是Call的拓展方法await,然后java第二个参数,给suspendCancellableCoroutine接收,这个await方法就是回调转成挂起函数的经典模板。这个模板代码一行都没精简过哈。

Q: 那协程请求完后切换到主线程在哪里执行的呢? image.png

看代码,关注下lifecycleScope的launch是否有主线程调度器,

val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope
    get() = lifecycle.coroutineScope
val Lifecycle.coroutineScope: LifecycleCoroutineScope
    get() {
        while (true) {
            val existing = mInternalScopeRef.get() as LifecycleCoroutineScopeImpl?
            if (existing != null) {
                return existing
            }
            val newScope = LifecycleCoroutineScopeImpl( this,
            //协程上下文在这里
            SupervisorJob() + Dispatchers.Main.immediate)
            if (mInternalScopeRef.compareAndSet(null, newScope)) {
                newScope.register()
                return newScope
            }
        }
    }  
复制代码

协程上下文:SupervisorJob() + Dispatchers.Main.immediate,从这里看出他是个Supervisor,主从作用域,它取消了,不会影响父协程,非常的可以。
调度器是这个Dispatchers.Main.immediate,还能说啥,协程的调度通过协程拦截器拦截Continuation实现。

image.png image.png kotlin的代码debug不好整,大家大致看下。

image.png

五、后续

我们看到用retrofit+协程写个接口请求,还要显式的try catch,真的是,有点,哎,看后续Retrofit支持Kotlin的Flow不,不支持的话,可以考虑自己整个试试。

分类:
Android
标签:
分类:
Android
标签: