Android 网络框架--Retrofit2 源码分析(下)

承接上篇我们讲了Retrofit获取一个对OkHttpCall的封装对象(Call对象)的过程,调用该对象的请求方法就可以执行同步或异步请求了。上篇为了让大家先从整体流程熟悉Retrofit,有些细节没有具体讲解,本篇对一下几点进行源码分析:

  • 怎么对OKHttp进行封装的,即OkHttpCall类。
  • 怎么创建OKHttp请求的Request对象的,即怎么解析注解,组织一个完整的Request。
  • Converter的创建、调用、实现。

OkHttpCall 源码分析

OkHttpCall是对OkHttp请求的一个封装,当调用OkHttpCall请求方法时,OkHttpCall主要做了三件事情:

  • 创建OkHttp的请求对象Call。
  • Call调用execute请求方法,完成请求。
  • 解析响应数据,返回结果。

我们以同步请求方法execute()来分析源码:

 @Override 
    public Response execute() throws IOException {
        okhttp3.Call call;
        
            ......//异常处理
        
            call = rawCall;
            if (call == null) {
                try {
                    //创建一个OKHttp的Call
                    call = rawCall = createRawCall();
                } catch (IOException | RuntimeExceptione) {
                    creationFailure = e;
                    throw e;
                }
            }
        }
        if (canceled) {
            call.cancel();
        }
        //执行请求,解析Response,返回结果
        return parseResponse(call.execute());
    }

异步求方法enqueue过程大致相同,不再贴代码。 在这里提醒大家的一点:Retrofit中也定义了一个Call接口,也是用作请求封装的,跟OkHttp的Call接口同名,大家注意区分。至于OkHttp的知识我们后续专门文章跟大家一块学习。

进一步看下createRawCall()方法如何创建Call的。

private okhttp3.Call createRawCall() throws IOException {    
    // 创建request    
    Request request = serviceMethod.toRequest(args);    
    // 创建call,其实就是一个RealCall    
    okhttp3.Call call = serviceMethod.callFactory.newCall (request);    
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");    
      }    
      return call; 
 }  

创建OKHttp的Call需要创建一个Request,然后传给callFactory创建一个Call对象,我们找到callFactory初始化的地方,看下callFactory是什么:

.....
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
  callFactory = new OkHttpClient();
}
.....

callFactory就是OKHttp中的OkHttpClient。Call创建完,剩下的工作就交给OKHttp了。
至此,我们分析了请求封装流程,并且知道调用创建Request对象的地方,接下来看Request对象怎么创建。

创建OKHttp请求的Request对象

Requst的创建主要涉及到类有:

  • RequestBuilder:Request构造器,用来构造Request。
  • ServiceMethod:解析请求接口方法的注解,包括方法注解信息、参数注解信息。
  • ParameterHandler:参数处理器,用来处理参数注解信息和实际传入参数数据,并将处理后的参数数据传递给RequestBuilder,用作构造Request对象。

从ServiceMethod的toRequest(args)方法看起:

Request toRequest(@Nullable Object... args) throws IOException {
        //创建RequestBuilder 对象
        RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,contentType, hasBody, isFormEncoded, isMultipart);
        
         ......//异常处理
         
        //遍历参数处理器parameterHandlers来处理每个参数定义和具体参数args[p]
        for (int p = 0; p < argumentCount; p++) {
            handlers[p].apply(requestBuilder, args[p]);
        }
        return requestBuilder.build();
    }
  • 创建一个RequestBuilder。
  • 遍历参数处理器parameterHandlers来处理每个参数定义和具体参数args[p]。
  • 最后通过请求构造器RequestBuilder 构造一个Request。

RequestBuilder 初始化传入的一串参数和parameterHandlers都是在ServiceMethod初始化的时候,解析请求接口方法的注解得到的。分为方法注解解析和参数注解解析,下面具体看一下:

注解解析
方法注解解析:

解析方法注解的方法是parseMethodAnnotation(Annotation annotation) :

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.");
                }
    ......//各种方法注解的判断和解析,不都贴出
  }

对方法注解进行解析,继续看到parseHttpMethodAndPath方法:

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);
            }
            //获得请求类型,是否有body数据
            this.httpMethod = httpMethod;
            this.hasBody = hasBody;

            if (value.isEmpty()) {
                return;
            }
       ......
            this.relativeUrl = value;
            this.relativeUrlParamNames = parsePathParameters(value);
        }

这样就得到httpMethod 、hasBody 、relativeUrl等 构造RequestBuilder 所需要的部分参数数据,其他方法注解解析同理,不再批量贴代码。

参数注解解析:

参数标解析parseParameter方法,同样是ServiceMethod初始化时调用,返回的是第p个参数的处理器parameterHandlers[p],代码如下

{
this.parameterHandlers[p] = parseParameter(p,parameterType,parameterAnnotations);
}

parseParameter方法又调用到parseParameterAnnotation实现参数注解解析,注解太多了,就以常用的Query注解,看下主要代码,其他注解同理:

private ParameterHandler parseParameterAnnotation(
 int p, Type type, Annotation[] annotations, Annotation annotation) {
      .......
       if (annotation instanceof Query) {
           //解析注解数据value、encoded
           Query query = (Query) annotation;
           String name = query.value();
           boolean encoded = query.encoded();
           Class rawParameterType = Utils.getRawType(type);
           gotQuery = true;
           //创建请求转换器
           Converter converter =  retrofit.stringConverter(type, annotations);
           //创建参数处理器的具体实现类
           return new ParameterHandler.Query<>(name, converter, encoded);               
          }
   ........

Query解析:

  • 判断为Query注解,拿到Query的value。
  • 创建了对应该Query注解的一个Converter。
  • 最后创建并返回了ParameterHandler.Query。

Query是ParameterHandler的实现类,并且是对应Query注解的参数处理类。其他参数注解同理,都有一个ParameterHandler实现类与之对应。

----------------------------------注解解析完成--------------------------------
注解解析讲解完成,回头看toRequest方法中调用ParameterHandler的apply方法,还是ParameterHandler.Query为例:

Override void apply(RequestBuilder builder, @Nullable T value) throws IOException {
      if (value == null) return; 
      //将请求传入的参数值转换成String
      String queryValue = valueConverter.convert(value);
      if (queryValue == null) return; 
      //参数数据添加给RequestBuilder
      builder.addQueryParam(name, queryValue, encoded);
}

apply方法,将传入的参数值通过Converter转换成字符串类型,作为get请求url参数的value,name就是get请求url参数的key,然后添加给RequestBuilder 。

继续看到addQueryParam方法:

void addQueryParam(String name, @Nullable String value, boolean encoded) {

    ......//异常处理
    
    if (encoded) {
      urlBuilder.addEncodedQueryParameter(name, value);
    } else {
      urlBuilder.addQueryParameter(name, value);
    }
  }

最终参数信息添加到 urlBuilder对象中,其他参数注解同理都是通过ParameterHandler的apply方法处理后添加到RequestBuilder中保存,用于Requst构建。

RequestBuilder所有数据准备完毕以后调用build方法,最终构建Request:

Request build() {
    HttpUrl url;
    //构造完整的Url
    HttpUrl.Builder urlBuilder = this.urlBuilder;
    if (urlBuilder != null) {
      url = urlBuilder.build();
    } else {
      url = baseUrl.resolve(relativeUrl);
      if (url == null) {
        throw new IllegalArgumentException(
            "Malformed URL. Base: " + baseUrl + ", Relative: " + relativeUrl);
      }
    }
    //构造完整的body
    RequestBody body = this.body;
    if (body == null) {
      if (formBuilder != null) {
        body = formBuilder.build();
      } else if (multipartBuilder != null) {
        body = multipartBuilder.build();
      } else if (hasBody) {
        body = RequestBody.create(null, new byte[0]);
      }
    }
    MediaType contentType = this.contentType;
    if (contentType != null) {
      if (body != null) {
        body = new ContentTypeOverridingRequestBody(body, contentType);
      } else {
        requestBuilder.addHeader("Content-Type", contentType.toString());
      }
    }
    //构造完整的Request
    return requestBuilder
        .url(url)
        .method(method, body)
        .build();
  }

build方法中,先构造完整的url和body,然后构建并返回完整的Request。

Request构造涉及到大量注解解析,繁琐但没有逻辑难度。因为不同注解意义不同,处理是有区别的,但是解析流程是和我们上面讲解的流程一样的。

Converter的创建、调用、实现

Converter按用途分为两类:

  • Converter responseConverter将响应数据转换成响应实体对象。
  • Converter 或者 Converter requestConverter,用来将请求实体对象转换成对应的请求数据。
responseConverter的创建、调用、实现

responseConverter的创建:
上篇我们已经讲到,ServiceMethod初始化时候调用了Retrofit中的

public  ConverterresponseBodyConverter(Type type, Annotation[] annotations) {
    return nextResponseBodyConverter(null, type, annotations);
  }

具体创建上篇中已讲到,是通过converter.Factory创建,不在赘述。

responseConverter的调用:
是在OkHttpCall 中解析响应数据parseResponse方法中调用的

 T body = serviceMethod.toResponse(catchingBody);

继续看到toResponse方法,

R toResponse(ResponseBody body) throws IOException {
        return responseConverter.convert(body);
}

responseConverter的实现:
以GsonResponseBodyConverter为例:

Override public T convert(ResponseBody value) throws IOException {
    JsonReader jsonReader =  gson.newJsonReader(value.charStream());
    try {
      return adapter.read(jsonReader);
    } finally {
      value.close();
    }
  }
requestConverter的创建、调用、实现

requestConverter的创建:
Query注解解析中,已经提到创建requestConverter。

      .......
  
      //创建请求转换器
      Converter converter = retrofit.stringConverter(type, annotations);

     ........

调用到Retrofit的stringConverter方法,具体创建也是通过converter.Factory创建,不再赘述。

requestConverter的调用:
是在ParameterHandler中参数处理apply方法中调用的。参考上面的代码。

requestConverter的实现:
以GsonRequestBodyConverter为例:

@Override public RequestBody convert(T value) throws IOException {
    Buffer buffer = new Buffer();
    Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
    JsonWriter jsonWriter = gson.newJsonWriter(writer);
    adapter.write(jsonWriter, value);
    jsonWriter.close();
    return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
  }

将请求实体对象转换成RequestBody。

注意

  • 本文是对上篇源码分析的补充分析, 请先对上篇的Retrofit核心流程熟悉,再看本文。
  • 本文按照代码阅读的顺序来分析的源码,从OkHttpCall 讲到的request创建、注解解析、convert使用。实际调用顺序不是这样,比如注解解析是在ServiceMethod创建时完成的,是在request创建之前。
  • 本文为了突出主要源码,略掉异常处理的代码,这部分代码大部分是用作检查请求接口的规范性。

你可能感兴趣的:(Android 网络框架--Retrofit2 源码分析(下))