为了账号安全,请及时绑定邮箱和手机立即绑定

Retrofit源码解析(二):源码解析

标签:
Android

前言

之前我们已经复习了Retrofit的基础用法,Retrofit的源码理解并不复杂,他实现的主要功能就是把接口文件通过注解转化成Okhttp请求,所以我们弄懂了主线,整个Retrofit我们就明白了。

正文

首先复习一下Retrofit的用法:

val retrofit = Retrofit.Builder()                // 必填项
                .baseUrl("http://www.baidu.com")
                .client(OkHttpClient())                // 对得到的结果进行转换,常用的有加密解密,json转换等等
                .addConverterFactory(StringConvertFactory())                // 对返回的结果进行封装,常用的有之间转化成Rxjava对象
                // 这里我们简单的进行包装
                .addCallAdapterFactory(ResponseWrapperCallAdapterFactory())
                .build()

api = retrofit.create(TestApi::class.java)

Retrofit的通过构建者模式创建,这里的重点是:

api = retrofit.create(TestApi::class.java)

我们看看这个create方法是怎么解析接口文件的:

public <T> T create(final Class<T> service) {    // 检查创建的网络请求的服务接口是否可用
    Utils.validateServiceInterface(service);    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }    // 当调用定义的接口方法的时候,通过动态代理调用自己的逻辑,发起网络请求
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {            // If the method is a method from Object then defer to normal invocation.
            // 如果参数的类型是Object,直接反射调用
            if (method.getDeclaringClass() == Object.class) {              return method.invoke(this, args);
            }            // 如果是default方法,根据平台类型直接调用
            // android平台固定返回false
            if (platform.isDefaultMethod(method)) {              return platform.invokeDefaultMethod(method, service, proxy, args);
            }            // 通过Method,封装成okhttp请求
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);            // 发起网络请求
            return serviceMethod.adapt(okHttpCall);
          }
        });
  }

通过create方法,创建了指定接口文件的动态代理,每次我们通过动态代理访问对应的方法的时候,都会进入到invoke方法,通过invoke方法,我们可以获得这个方法的注解信息,然后转换成okhttp请求。

首先检查了接口文件是否合法,判断是否是interface接口并且方法书大于0。这里我们还看到了构建Retrofit时设置的validateEagerly属性,如果是true,会在创建接口代理时,解析所有的方法,所以我们一般不会开启这个设置。eagerlyValidateMethods方法内部调用的是loadServiceMethod方法,loadServiceMethod方法解析调用方法的注解信息,封装成ServiceMethod对象。

ServiceMethod<?, ?> loadServiceMethod(Method method) {    // 如果缓存中已经有了这个方法,直接返回
    ServiceMethod<?, ?> result = serviceMethodCache.get(method);    if (result != null) return result;    // 同步锁
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);      if (result == null) {        // 把这个方法封装成ServiceMethod对象,保存起来
        result = new ServiceMethod.Builder<>(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }    return result;
  }

首先检查缓存,如果缓存了这个方法的请求信息,直接返回,否则会重新解析一次,解析的步骤被封装到ServiceMethod.Builder中,所以我们看一下ServiceMethod.Builder的build方法。

public ServiceMethod build() {      // 创建callAdapter
      callAdapter = createCallAdapter();      // 得到返回值的类型
      responseType = callAdapter.responseType();      if (responseType == Response.class || responseType == okhttp3.Response.class) {        throw methodError("'"
            + Utils.getRawType(responseType).getName()
            + "' is not a valid response body type. Did you mean ResponseBody?");
      }      // 创建responseConverter
      responseConverter = createResponseConverter();      // 解析注解
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }      // 检查是否设置了请求方式
      if (httpMethod == null) {        throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
      }      // 如果没有添加body,要检查请求的方式是否支持无body请求
      if (!hasBody) {        if (isMultipart) {          throw methodError(              "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
        }        if (isFormEncoded) {          throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
              + "request body (e.g., @POST).");
        }
      }      // 遍历参数的注解
      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);
      }      if (relativeUrl == null && !gotUrl) {        throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
      }      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {        throw methodError("Non-body HTTP method cannot contain @Body.");
      }      if (isFormEncoded && !gotField) {        throw methodError("Form-encoded method must contain at least one @Field.");
      }      if (isMultipart && !gotPart) {        throw methodError("Multipart method must contain at least one @Part.");
      }      return new ServiceMethod<>(this);
    }

在build方法中,开始了对注解的解析,这个还做了很多的判断,例如是否设置了url的检查,某些注解使用冲突的问题等等。这个方法中主要看parseMethodAnnotation方法和parseParameter方法。

parseMethodAnnotation方法用来解析方法的注解:

private void parseMethodAnnotation(Annotation annotation) {      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);        if (!Void.class.equals(responseType)) {          throw methodError("HEAD method must use Void as response type.");
        }
      } 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 = (HTTP) annotation;
        parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
      } else if (annotation instanceof retrofit2.http.Headers) {
        String[] headersToParse = ((retrofit2.http.Headers) annotation).value();        if (headersToParse.length == 0) {          throw methodError("@Headers annotation is empty.");
        }
        headers = parseHeaders(headersToParse);
      } else if (annotation instanceof Multipart) {        if (isFormEncoded) {          throw methodError("Only one encoding annotation is allowed.");
        }
        isMultipart = true;
      } else if (annotation instanceof FormUrlEncoded) {        if (isMultipart) {          throw methodError("Only one encoding annotation is allowed.");
        }
        isFormEncoded = true;
      }
    }

这个分别解析了方法的注解,我们看到了熟悉@GET、@POST等注解,具体的实现在parseHttpMethodAndPath中:

/**
     * 解析方法中注解,保存对应的value
     * */
    private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {      if (this.httpMethod != null) {        throw methodError("Only one HTTP method is allowed. Found: %s and %s.",            this.httpMethod, httpMethod);
      }      this.httpMethod = httpMethod;      this.hasBody = hasBody;      if (value.isEmpty()) {        return;
      }      // Get the relative URL path and existing query string, if present.
      // 判断url链接之后的参数是否合法
      int question = value.indexOf('?');      if (question != -1 && question < value.length() - 1) {        // Ensure the query string does not have any named parameters.
        String queryParams = value.substring(question + 1);
        Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);        if (queryParamMatcher.find()) {          throw methodError("URL query string \"%s\" must not have replace block. "
              + "For dynamic query parameters use @Query.", queryParams);
        }
      }      // 保存相对地址
      this.relativeUrl = value;      // 从地址中分离出设置的参数
      this.relativeUrlParamNames = parsePathParameters(value);
    }

parseHttpMethodAndPath先获取注解中网络请求的相对路径,如果设置了相对地址,最后会和baseUrl拼接成完成的请求地址,另外还检查了在相对地址中,是不允许设置参数的,考虑到有些对url链接的地址组成不了解朋友,我们举一个简单的例子:

地址:http://www.baidu.com

如果想要在网址中添加参数,需要在末尾加上?,然后通过 key=value&key=value的方式拼接

拼接参数后:http://www.baidu.com/?wd=111

具体的url了解的格式大家可以自己查看相关资料。看完了解析方法上的直接,接着看解析方法中参数的注解,因为代码实在是太多了,我们就挑其中一个熟悉的注解作为理解的例子:

else if (annotation instanceof Field) {        if (!isFormEncoded) {          throw parameterError(p, "@Field parameters can only be used with form encoding.");
        }
        Field field = (Field) annotation;        String name = field.value();
        boolean encoded = field.encoded();

        gotField = true;

        Class<?> rawParameterType = Utils.getRawType(type);        // 如果是集合,需要指定泛型是String
        if (Iterable.class.isAssignableFrom(rawParameterType)) {          if (!(type instanceof ParameterizedType)) {            throw parameterError(p, rawParameterType.getSimpleName()
                + " must include generic type (e.g., "
                + rawParameterType.getSimpleName()
                + "<String>)");
          }
          ParameterizedType parameterizedType = (ParameterizedType) type;
          Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
          Converter<?, String> converter =
              retrofit.stringConverter(iterableType, annotations);          return new ParameterHandler.Field<>(name, converter, encoded).iterable();
        } 
        // 数组
        else if (rawParameterType.isArray()) {
          Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
          Converter<?, String> converter =
              retrofit.stringConverter(arrayComponentType, annotations);          return new ParameterHandler.Field<>(name, converter, encoded).array();
        } 
        // 其他
        else {
          Converter<?, String> converter =
              retrofit.stringConverter(type, annotations);          return new ParameterHandler.Field<>(name, converter, encoded);
        }

      }

我们看到@Field注解必须和@FormUrlEncoded一起使用,把对应的注解的类型的key和设置的参数值加入到表单中去。

现在方法的注解和参数的注解都已经得到了,Retrofit把ServiceMethod和指定的参数封装成OKhttpCall:

OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);

OkhttpCall是ServiceMethod和okhttp请求转换的处理类,他主要是负责成request和处理response,这里贴出几个重要的方法:

生成okhttp的网络请求:

/** 
 * OKhttpCall.toCall实际上调用的ServiceMethod.toCall方法
 * 生成okhttp网络请求
 */
  okhttp3.Call toCall(@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 + ")");
    }    // 把参数添加到requestBuilder中
    for (int p = 0; p < argumentCount; p++) {
      handlers[p].apply(requestBuilder, args[p]);
    }    // 调用okhttpClient创建Call对象
    return callFactory.newCall(requestBuilder.build());
  }

获取okhttp的request信息:

@Override public synchronized Request request() {    // 如果已经有okhttp的call请求,返回call的request
    okhttp3.Call call = rawCall;    if (call != null) {      return call.request();
    }    // 判断是否创建请求失败过
    if (creationFailure != null) {      if (creationFailure instanceof IOException) {        throw new RuntimeException("Unable to create request.", creationFailure);
      } else if (creationFailure instanceof RuntimeException) {        throw (RuntimeException) creationFailure;
      } else {        throw (Error) creationFailure;
      }
    }    try {      // 创建okhttp请求
      return (rawCall = createRawCall()).request();
    } catch (RuntimeException | Error e) {
      throwIfFatal(e); // Do not assign a fatal error to creationFailure.
      creationFailure = e;      throw e;
    } catch (IOException e) {
      creationFailure = e;      throw new RuntimeException("Unable to create request.", e);
    }
  }

解析okhttp的请求结果:

@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();
        } 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());
  }

其中在解析网络请求结果的过程中,调用了我们设置的ConvertFactory,对得到的响应结果进行转换:

/** Builds a method return value from an HTTP response body. */
  /**
   * 对得到的网络请求结果进行转换
   * */
  R toResponse(ResponseBody body) throws IOException {    return responseConverter.convert(body);
  }

到此为止Retrofit的发起网络请求的整个流程就结束了。

哎?是不是忘了点什么?没错那就是CallAdapter,其实CallAdapter只是对网络请求结果的包装,也就对ConvertFactory的转换结果的封装,回顾一下我们自定义的CallAdapterFactory:

/**
 * Created by li.zhipeng on 2018/8/29.
 *
 *  把得到网络请求结果String,转换成ResponseWrapper
 */public class ResponseWrapperCallAdapterFactory extends CallAdapter.Factory  {    @Override
    public CallAdapter<String, ResponseWrapper> get(@NonNull final Type returnType, @NonNull Annotation[] annotations, @NonNull Retrofit retrofit) {        return new CallAdapter<String, ResponseWrapper>() {            @Override
            public Type responseType() {                return returnType;
            }            @Override
            public ResponseWrapper adapt(@NonNull Call<String> call) {                try {                    return new ResponseWrapper(call.execute().body());
                } catch (IOException e) {
                    e.printStackTrace();
                }                return new ResponseWrapper("error");
            }
        };
    }
}

这个类的重点就是:

@Override
            public ResponseWrapper adapt(@NonNull Call<String> call) {                try {                    return new ResponseWrapper(call.execute().body());
                } catch (IOException e) {
                    e.printStackTrace();
                }                return new ResponseWrapper("error");
            }

我们在CallAdapter中开启了网络请求,并且把得到的结果封装成我们需要的对象。在动态代理中

serviceMethod.adapt(okHttpCall);
T adapt(Call<R> call) {    return callAdapter.adapt(call);
  }

通过ServiceMethod间接调用了我们设置的CallAdapter的adapt方法。



作者:珠穆朗玛小王子
链接:https://www.jianshu.com/p/fd02cf568fb0


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消