博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Retrofit 阅读笔记
阅读量:7041 次
发布时间:2019-06-28

本文共 12874 字,大约阅读时间需要 42 分钟。

前言

Retrofit 是现在 Android 主流的网络请求库,对比 Volley 它解耦更加彻底,使用更加方便,而且支持 RxJava。具体的区别的可以看看这篇文章——。

Retrofit 中文可以翻译成 在原有基础上改进。它的底层基于 OkHttp。

Retrofit 的精简流程图,图片来自Stay:

Retrofit 中使用了大量设计模式,分析源码之前最好先熟悉一下这些模式。

  1. 建造者模式
  2. 工厂模式
  3. 外观模式
  4. 策略模式
  5. 适配器模式
  6. 装饰模式
  7. 代理模式及 Java 动态代理

还涉及一些基础的 Java 注解的知识,建议先打好基础再分析源码。

Retrofit的类的结构

上层有4个抽象接口,有默认的实现类。核心服务类是ServiceMethod。Platform判断Android,Java平台。

Call

/** * 这是一个Retrofit方法给服务器发送一个request,返回一个response的调用。 * 每个Call生产一组HTTP request和response。对于同一个完全一样请求,实现 * clone方法来创建多个call,这个可以用于轮询和错误重试的场景。 * * Calls 同步执行的时候使用 execute(), 异步执行使用 enqueue()。无论是 * 同步还是异步都可以在请求的时候使用 cancel() 随时被取消。正在写入request或者读取response的Call可能会引起IOException。 * * * @param 
请求成功时返回的 response body 类型 */public interface Call
extends Cloneable { // 同步发起请求返回response Response
execute() throws IOException; // 异步发起请求,结果返回给回调 void enqueue(Callback
callback); // 如果call已经调用了execute()或者enqueue()就返回true。不允许一个call重复请求。 boolean isExecuted(); // 取消正在执行中的请求,如果call还没开始执行请求,就不做任何处理。 void cancel(); // 是否取消了请求 boolean isCanceled(); // 创建一个新的和当前完全一样的call Call
clone(); // 原始的HTTP请求 Request request();}复制代码

CallAdapter

用于RxJava的转化。

public interface CallAdapter
{ // 返回解析成java对象的response的类型。 Type responseType(); T adapt(Call
call); // 抽象工厂类 abstract class Factory { public abstract CallAdapter
get(Type returnType, Annotation[] annotations, Retrofit retrofit); protected static Type getParameterUpperBound(int index, ParameterizedType type) { return Utils.getParameterUpperBound(index, type); } protected static Class
getRawType(Type type) { return Utils.getRawType(type); } }}复制代码

Callback

public interface Callback
{ void onResponse(Call
call, Response
response); void onFailure(Call
call, Throwable t);}复制代码

Converter

public interface Converter
{ // 实体类和HTTP的RequestBody和ResponseBody的互相转换 T convert(F value) throws IOException; // 抽象工厂 abstract class Factory { public Converter
responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { return null; } public Converter
requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { return null; } public Converter
stringConverter(Type type, Annotation[] annotations, Retrofit retrofit) { return null; } }}复制代码

正常请求网络的套路

如果我们自己写一个网络请求模块一般是这样的套路:

  1. build request 参数,加入到请求队列中
  2. 在子线程轮询执行
  3. 得到服务器数据后,回调给上层

Retrofit 不外乎也是这种套路,那么它到底有什么精妙的地方呢?

举个栗子

先看一个正常 Retrofit 请求的例子:

Retrofit build = new Retrofit.Builder().baseUrl(ServiceApi.BASE_URL)        .addConverterFactory(GsonConverterFactory.create())        .build();    ServiceApi serviceApi = build.create(ServiceApi.class);    serviceApi.getHistoryDate().enqueue(new Callback
() { @Override public void onResponse(Call
call, Response
response) { Log.d(TAG, "onResponse- " + response.body()); } @Override public void onFailure(Call
call, Throwable t) { Log.d(TAG, "onFailure- " + t.getMessage()); } }); /** * API 来自 gank.io,感谢 @代码家 */interface ServiceApi { String BASE_URL = "http://gank.io/api/"; /** * 获取某一天的数据 */ @GET("day/{year}/{month}/{day}") Call
getDataOnSomeday( @Path("year") String year, @Path("month") String month, @Path("day") String day);}复制代码

我们通过调用 serviceApi.getHistoryDate().enqueue(callback) 请求数据并在回调中处理数据。 就从这行代码作为切入点,看看发出请求的时候到底内部发生了啥? debug serviceApi.getHistoryDate() 这行代码我发现,运行至此的时候调用了动态代理,代码走到了下面的 create(final Class<T> service) 中的 InvocationHandler() 中,回调了 invoke()

我们来仔细分析一下这个函数。

/** * 创建由 service 定义的 API 的实现。 * return  类型还是为 T 的代理对象	 */public 
T create(final Class
service) { // 检查 service 是否合法,必须是接口且只有 1 个接口, 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, Object... args) throws Throwable { // If the method is a method from Object then defer to normal invocation. if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } ServiceMethod serviceMethod = loadServiceMethod(method); OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall); } }); }复制代码

很明显这是一个 Java 动态代理。这个方法在 ServiceApi serviceApi = build.create(ServiceApi.class); 调用,返回的是一个代理对象。

invoke(Object proxy, Method method, Object... args)中,Retrofit 只关心 method 和 args 两个参数。 核心代码是这 3 句:

ServiceMethod serviceMethod = loadServiceMethod(method);OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);return serviceMethod.callAdapter.adapt(okHttpCall); 复制代码

ServcieMethod

ServiceMethod就像是一个中央处理器,传入Retrofit对象和Method对象,调用各个接口和解析器,最终生成一个Request,包含api 的域名、path、http请求方法、请求头、是否有body、是否是multipart等等。最后返回一个Call对象,Retrofit2中Call接口的默认实现是OkHttpCall,它默认使用OkHttp3作为底层http请求client。

使用Java动态代理的目的就要拦截被调用的Java方法,然后解析这个Java方法的注解,最后生成Request由OkHttp发送。

先看 loadServiceMethod(method),从这里面得到一个 ServiceMethod

ServiceMethod loadServiceMethod(Method method) {    ServiceMethod result;    synchronized (serviceMethodCache) {      // 先从缓存中取      result = serviceMethodCache.get(method);      if (result == null) {      	// 如果没有就新建,然后加入缓存        result = new ServiceMethod.Builder(this, method).build();        serviceMethodCache.put(method, result);      }    }    return result;  }复制代码

build方法里面创建了CallAdapter和Converter。处理注解,将其转化成OkHttp Call。

/**  * 创建 CallAdapter,创建 Converter,解析注释  * ServiceMethod 的作用是将接口方法的调用适配为 HTTP Call  * return ServiceMethod 对象  */public ServiceMethod build() {              // 创建callAdapter       callAdapter = createCallAdapter();       responseType = callAdapter.responseType();       // 创建Converter       responseConverter = createResponseConverter();       // 遍历,解析方法注释       for (Annotation annotation : methodAnnotations) {           parseMethodAnnotation(annotation);       }              // 参数里的注解的个数       int parameterCount = parameterAnnotationsArray.length;       // 创建对应的 parameterHandlers数组。将每个参数的注解解析成parameterHandler对象存入数组。       parameterHandlers = new ParameterHandler
[parameterCount]; for (int p = 0; p < parameterCount; p++) { Type parameterType = parameterTypes[p]; Annotation[] parameterAnnotations = parameterAnnotationsArray[p]; parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations); } return new ServiceMethod<>(this); }复制代码

这里的parameterHandlers 负责解析API定义时每个方法的参数,并在构造HTTP请求时设置参数。

解析方法注释

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);    }        // ...省略代码}复制代码
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {           this.httpMethod = httpMethod;      this.hasBody = hasBody;      if (value.isEmpty()) {        return;      }      // Get the relative URL path and existing query string, if present.      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);      }      this.relativeUrl = value;      this.relativeUrlParamNames = parsePathParameters(value);}static Set
parsePathParameters(String path) { Matcher m = PARAM_URL_REGEX.matcher(path); Set
patterns = new LinkedHashSet<>(); while (m.find()) { patterns.add(m.group(1)); } return patterns;}复制代码

parameterHandlers

每个参数都会有一个 ParameterHandler,由 ServiceMethod#parseParameter 方法负责创建,其主要内容就是解析每个参数使用的注解类型(诸如 Path,Query,Field 等),对每种类型进行单独的处理。构造 HTTP 请求时,我们传递的参数都是字符串,那 Retrofit 是如何把我们传递的各种参数都转化为 String 的呢?还是由 Retrofit 类提供 converter!

Converter.Factory 除了提供上一小节提到的 responseBodyConverter,还提供 requestBodyConverter 和 stringConverter,API 方法中除了 @Body 和 @Part 类型的参数,都利用 stringConverter 进行转换,而 @Body 和 @Part 类型的参数则利用 requestBodyConverter 进行转换。

CallAdapter

// #ServiceMethod.javathis.callFactory = builder.retrofit.callFactory();复制代码

CallAdapter由retrofit提供,我们可以自己指定,默认是okhttp3.OkHttpClient。

创建callAdapter由retrofit完成。

private CallAdapter
createCallAdapter() { return (CallAdapter
) retrofit.callAdapter(returnType, annotations);} // # Retrofit.java public CallAdapter
callAdapter(Type returnType, Annotation[] annotations) { return nextCallAdapter(null, returnType, annotations); }复制代码

最终走到nextCallAdapter(),通过CallAdapter.Factory来创造。

public CallAdapter
nextCallAdapter(CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) { // ...省略多若干码 // 去除skipPast这个过去的工厂,Retrofit默认传的是null。 int start = adapterFactories.indexOf(skipPast) + 1; // 遍历取出第一个工厂,获得adapter。 for (int i = start, count = adapterFactories.size(); i < count; i++) { CallAdapter
adapter = adapterFactories.get(i).get(returnType, annotations, this); if (adapter != null) { return adapter; } }}复制代码

ResponseConverter

和callAdapter一样,也是由Retrofit创建的。通过遍历 Converter.Factory 列表,看看有没有工厂能够提供需要的 responseBodyConverter。工厂列表同样可以在构造 Retrofit 对象时进行添加。

OkHttpCall

OkHttpCall实现了Call接口。

okHttpCall 关键的部分是:

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;  }          // ServiceMethod.java # toRequest()   /** Builds an HTTP request from method arguments. */  Request toRequest(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[] handlers = (ParameterHandler[]) parameterHandlers;        // 省略部分检查代码        for (int p = 0; p < argumentCount; p++) {      handlers[p].apply(requestBuilder, args[p]);    }    return requestBuilder.build();  }复制代码

这里由serviceMethod创建request,之前的ParameterHandler这里就用到了,这里把参数传进去一起组成完整request。并且由serviceMethod里的callFactory创建一个Call,默认就是OkHttpCall。

调用execute()执行同步请求:

@Override public Response
execute() throws IOException { okhttp3.Call call; // 省略判断代码... call = rawCall = createRawCall(); // 省略判断代码... // 最终调用 parseResponse 解析返回的结果 return parseResponse(call.execute()); } Response
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 { // 这里调用serviceMethod中的方法用对应的ConvertFactory转换 T body = serviceMethod.toResponse(catchingBody); return Response.success(body, rawResponse); } catch (RuntimeException e) { // If the underlying source threw an exception, propagate that rather than indicating it was // a runtime exception. catchingBody.throwIfCaught(); throw e; } }复制代码

参考资料

转载地址:http://laxal.baihongyu.com/

你可能感兴趣的文章
中国好DC(数据中心)
查看>>
推动网络流量全面可视化 Gigamon在行动
查看>>
大数据时代继续教育深化发展的机遇与挑战分析
查看>>
黑客“纵横”全球金融系统 中国公司被盯上
查看>>
“光伏贷”推动分布式光伏进入百姓家
查看>>
探究电气设计系统中计算机的应用
查看>>
洛龙区:加快布局大数据产业
查看>>
看不见的"频谱"助力智慧城市建设
查看>>
软件测试文档写作——测试方案
查看>>
大数据的商业化:从数据、模型到业务逻辑
查看>>
Junit在MyEclipse上怎么用?
查看>>
能测试知多少--系统计数器与硬件分析
查看>>
颠覆传统 移动CRM成企业应用热点
查看>>
适合应用RFID的六大领域介绍
查看>>
《Web测试囧事》——2.6 时区不一致造成邮件发送异常
查看>>
需求管理是需求开发的基础
查看>>
干货:模板网站SEO优化技巧!
查看>>
CB Insights:2017年Q1网络安全领域共实现140宗投资
查看>>
安捷伦2016 Q2收入较去年增长6% 调升全年收入指导范围
查看>>
最新 Chrome 可让本地文件在网页应用中打开
查看>>