Retrofit源码流程解析(二)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_18242391/article/details/80209465

        上一篇讲了Retrofit的请求流程,对框架的大致调用过程做了了解,地址在这里Retrofit源码流程解析(一),这一篇打算讲解具体调用Okhttp3的过程,以及数据请求完成对数据的解析过程。这一过程都集中在OkHttpCall这个类中,更将详细的话还是通过它回调到其它类中。接下来还是结合具体源码来讲解。我们知道Retrofit有execute同步和enqueue异步两种请求方式,分别都会调用到OkHttp3这两个方法中,那么我们首先看第一个方法。

execute

@Override public Response<T> execute() throws IOException {
  okhttp3.Call call; 

  synchronized (this) {
    if (executed) throw new IllegalStateException("Already executed.");
    executed = true;

    if (creationFailure != null) {
      if (creationFailure instanceof IOException) {
        throw (IOException) creationFailure;
      } else if (creationFailure instanceof RuntimeException) {
        throw (RuntimeException) creationFailure;
      } else {
        throw (Error) creationFailure;
      }
    }

    call = rawCall;
    if (call == null) {
      try {
        call = rawCall = createRawCall();  //1
      } catch (IOException | RuntimeException | Error e) {
        throwIfFatal(e); //  Do not assign a fatal error to creationFailure.
        creationFailure = e;
        throw e;
      }
    }
  }

  if (canceled) {
    call.cancel();
  }

  return parseResponse(call.execute());  //2
}

这段代码只用看标记处,其它基本都只是异常和条件的判断,先看第一处,通过返回值可以知道是创建请求对象call的,看它的具体方法

private okhttp3.Call createRawCall() throws IOException {
  Request request = serviceMethod.toRequest(args);
  okhttp3.Call call = serviceMethod.callFactory.newCall(request);
  if (call == null) {
    throw new NullPointerException("Call.Factory returned null.");
  }
  return call;
}

在这个方法里面首先通过toRequest创建一个Request对象,它是OkHttp3里面的对象,是用来封装网络请求参数的,它的具体实现在ServiceMethod中。

/** Builds an HTTP request from method arguments. */
Request toRequest(@Nullable Object... args) throws IOException {
  RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
      contentType, hasBody, isFormEncoded, isMultipart);

  @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
  ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

  int argumentCount = args != null ? args.length : 0;
  if (argumentCount != handlers.length) {
    throw new IllegalArgumentException("Argument count (" + argumentCount
        + ") doesn't match expected count (" + handlers.length + ")");
  }

  for (int p = 0; p < argumentCount; p++) {
    handlers[p].apply(requestBuilder, args[p]);
  }

  return requestBuilder.build();
}

可以看见就是对创建Request过程的封装,首先实例化一个RequestBuilder对象,里面封装了请求方法,url,以及其它表单相关参数。接着调用ParameterHandler的apply方法完成参数的设置,来看下ParameterHandler是在哪里赋值初始化的。这个属性的赋值是在ServiceMethod的build方法里面。

int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
  Type parameterType = parameterTypes[p];
  if (Utils.hasUnresolvableType(parameterType)) {
    throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
        parameterType);
  }

  Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
  if (parameterAnnotations == null) {
    throw parameterError(p, "No Retrofit annotation found.");
  }

  parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations); //1
}

parameterHandlers是个数组,它的长度是参数注解个数,parameterHandlers的赋值是在标注处。调用的parseParameter方法,里面最终会调用parseParameterAnnotation方法,这个方法是根据不同的注解参数实例化ParameterHandler对象。由于这个方法太长了,我只对其中一种情况做解释,其余的可以自行查看, 就举例annotation为path的情况

if (annotation instanceof Path) {
  if (gotQuery) {
    throw parameterError(p, "A @Path parameter must not come after a @Query.");
  }
  if (gotUrl) {
    throw parameterError(p, "@Path parameters may not be used with @Url.");
  }
  if (relativeUrl == null) {
    throw parameterError(p, "@Path can only be used with relative url on @%s", httpMethod);
  }
  gotPath = true;

  Path path = (Path) annotation;
  String name = path.value();
  validatePathName(p, name);

  Converter<?, String> converter = retrofit.stringConverter(type, annotations); //1
  return new ParameterHandler.Path<>(name, converter, path.encoded()); //2

}

最后一句实例化了一个ParameterHandler.Path对象,并且传了一个converter,这个converter是什么了,它的作用是将返回的response做相应的转化对象,我们可以通过converter将response直接转化为string返回,也可以解析成一个javabean,然后提供给用户直接使用,不需要手动解析,甚至可以定制化自己的converter对象。那么我们查看这个stringConverter的具体实现,它的实现在Retrofit中,看下具体做了那些操作

/**
 * Returns a {@link Converter} for {@code type} to {@link String} from the available
 * {@linkplain #converterFactories() factories}.
 */
public <T> Converter<T, String> stringConverter(Type type, Annotation[] annotations) {
  checkNotNull(type, "type == null");
  checkNotNull(annotations, "annotations == null");

  for (int i = 0, count = converterFactories.size(); i < count; i++) {
    Converter<?, String> converter =
        converterFactories.get(i).stringConverter(type, annotations, this);
    if (converter != null) {
      //noinspection unchecked
      return (Converter<T, String>) converter;
    }
  }

  // Nothing matched. Resort to default converter which just calls toString().
  //noinspection unchecked
  return (Converter<T, String>) BuiltInConverters.ToStringConverter.INSTANCE;
}

首先从设置的converterFactories里面去查找,如果有就返回符合的converter,没有符合条件的就返回

BuiltInConverters.ToStringConverter.INSTANCE

如果去里面查看convert方法,可以看见就是调用了toString()方法返回了一个String字符串。 

接下来看下

new ParameterHandler.Path<>(name, converter, path.encoded())

做了什么操作

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

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

  @Override void apply(RequestBuilder builder, @Nullable T value) throws IOException {
    if (value == null) {
      throw new IllegalArgumentException(
          "Path parameter \"" + name + "\" value must not be null.");
    }
    builder.addPathParam(name, valueConverter.convert(value), encoded);
  }
}

这段代码中,内部类Path继承了抽象类ParameterHandler,并实现了apply方法。同时可以查看其它的继承它的内部类


它们都是不同的方法注解对应的具体返回。

那么,我们继续回到ServiceMethod的toRequest方法,里面有这么一段

for (int p = 0; p < argumentCount; p++) {
  handlers[p].apply(requestBuilder, args[p]);
}
在这段代码中调用了ParameterHandler的apply方法,给requestBuilder设置args参数值,其实具体查看Path的apply方法,就是做了一个参数替换,将name替换成对应的value。到了这里可以知道,每一个参数注解对应一个ParameterHandler的具体实现,它的作用就是封装了参数name,value已经对应的处理方法。到了这里toRequest方法也就解释完毕了。

继续回到createRawCall方法

private okhttp3.Call createRawCall() throws IOException {
  Request request = serviceMethod.toRequest(args);
  okhttp3.Call call = serviceMethod.callFactory.newCall(request); //2
  if (call == null) {
    throw new NullPointerException("Call.Factory returned null.");
  }
  return call;
}

标注2处调用serviceMethod.callFactory.newCall方法,其实如果仔细看了Retrofit的builder方法,就可以知道callFactory的赋值是在这里,callFactory就是okhttpclient对象,它然后就去调用newCall,实际就是返回okhttp里面的call对象的实例,然后用call去做网络请求操作。

这部分完成了,接着继续看execute方法的最后一句

parseResponse(call.execute())

刚刚获取的call对象,然后去调用execute做同步请求,返回的response,然后传到parseResponse方法里面解析response

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
  ResponseBody rawBody = rawResponse.body();

  // Remove the body's source (the only stateful object) so we can pass the response along.
  rawResponse = rawResponse.newBuilder()
      .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
      .build();

  int code = rawResponse.code();
  if (code < 200 || code >= 300) {
    try {
      // Buffer the entire body to avoid future I/O.
      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);
  }

  ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
  try {
    T body = serviceMethod.toResponse(catchingBody);  //1
    return Response.success(body, rawResponse);  //2
  } catch (RuntimeException e) {
    // If the underlying source threw an exception, propagate that rather than indicating it was
    // a runtime exception.
    catchingBody.throwIfCaught();
    throw e;
  }
}

这段代码只需要看标记处,首先会调用serviceMethod的toReponse解析response

/** Builds a method return value from an HTTP response body. */
R toResponse(ResponseBody body) throws IOException {
  return responseConverter.convert(body);
}

这个responseConverter的赋值其实是调用retrofit的nextResponseBodyConverter方法

public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
    @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
  checkNotNull(type, "type == null");
  checkNotNull(annotations, "annotations == null");

  int start = converterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = converterFactories.size(); i < count; i++) {
    Converter<ResponseBody, ?> converter =
        converterFactories.get(i).responseBodyConverter(type, annotations, this);
    if (converter != null) {
      //noinspection unchecked
      return (Converter<ResponseBody, T>) converter;
    }
  }

  StringBuilder builder = new StringBuilder("Could not locate ResponseBody converter for ")
      .append(type)
      .append(".\n");
  if (skipPast != null) {
    builder.append("  Skipped:");
    for (int i = 0; i < start; i++) {
      builder.append("\n   * ").append(converterFactories.get(i).getClass().getName());
    }
    builder.append('\n');
  }
  builder.append("  Tried:");
  for (int i = start, count = converterFactories.size(); i < count; i++) {
    builder.append("\n   * ").append(converterFactories.get(i).getClass().getName());
  }
  throw new IllegalArgumentException(builder.toString());
}

代码表示得很清楚了,会遍历converterFactories集合的responseBodyConverter方法,根据type和annotation匹配符合条件的converter然后去调用它的convert,最后将转换的结果回调给调用者。

到了这里,execute的流程就完毕了。接下来就是enqueue方法了。

enqueue

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

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

  if (canceled) {
    call.cancel();
  }

  call.enqueue(new okhttp3.Callback() {
    @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
        throws IOException {
      Response<T> response;
      try {
        response = parseResponse(rawResponse);
      } catch (Throwable e) {
        callFailure(e);
        return;
      }
      callSuccess(response);
    }

    @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) {
        t.printStackTrace();
      }
    }

    private void callSuccess(Response<T> response) {
      try {
        callback.onResponse(OkHttpCall.this, response);
      } catch (Throwable t) {
        t.printStackTrace();
      }
    }
  });
}

其实刚刚讲了execute方法,而enqueue方法大致逻辑基本一样,只不过一个是同步一个是异步操作,enqueue调用的是okhttp的enqueue方法,成功之后也是会调用parseResponse方法去解析response,将解析的结果回调给retrofit的Callback回调方法。由于okhttp的异步操作回调是子线程,所以切换线程的操作还是由Retrofit来完成,至于怎么切换的第一篇文章已经讲解了,这里就不做过多的赘述了。

总结

Retrofit源码的讲解也就告一段落了,至于还有什么补充的,后面有新体会时候再来讲解了,其实Retrofit的源码还算简单的,多看几遍还是容易懂的,并且里面的各种设计模式以及解耦操作很值得学习,有时间的话还是值得多研究的。

猜你喜欢

转载自blog.csdn.net/qq_18242391/article/details/80209465