博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android进阶必学retrofit源码解析
阅读量:7121 次
发布时间:2019-06-28

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

源码的分析将从基本的使用方法入手,分析retrofit的实现方案,以及其中涉及到的一些有趣的技巧。

Android技术交流群653583088,欢迎大家加入交流,畅谈!本群有免费学习资料视频

简单使用

定义HTTP API

public interface GitHubService {  @GET("users/{user}/repos")  Call
> listRepos(@Path("user") String user);}复制代码

创建Retrofit并生成API的实现

Retrofit retrofit = new Retrofit.Builder()    .baseUrl("https://api.github.com/")    .build();GitHubService service = retrofit.create(GitHubService.class);复制代码

调用API方法,生成Call

Call
> repos = service.listRepos("octocat");复制代码

Retrofit的创建

retrofit实例的创建,使用了builder模式,从下面的源码中可以看出

public static final class Builder {	Builder(Platform platform) {		this.platform = platform;		converterFactories.add(new BuiltInConverters());	}	public Builder() {		// Platform.get()方法可以用于判断当前的环境		this(Platform.get());	}	public Builder baseUrl(String baseUrl) {      checkNotNull(baseUrl, "baseUrl == null");      HttpUrl httpUrl = HttpUrl.parse(baseUrl);      if (httpUrl == null) {        throw new IllegalArgumentException("Illegal URL: " + baseUrl);      }      return baseUrl(httpUrl);    }		public Retrofit build() {      if (baseUrl == null) {        throw new IllegalStateException("Base URL required.");      }      okhttp3.Call.Factory callFactory = this.callFactory;      if (callFactory == null) {        callFactory = new OkHttpClient();// 新建Client,留到之后newCall什么的      }      Executor callbackExecutor = this.callbackExecutor;      if (callbackExecutor == null) {        callbackExecutor = platform.defaultCallbackExecutor();      }      // Make a defensive copy of the adapters and add the default Call adapter.      List
adapterFactories = new ArrayList<>(this.adapterFactories); adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); // Make a defensive copy of the converters. List
converterFactories = new ArrayList<>(this.converterFactories); return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories, callbackExecutor, validateEagerly); }}复制代码

retrofit.create

好玩的地方开始了,我们先来看看这个方法

public 
T create(final Class
service) { Utils.validateServiceInterface(service); if (validateEagerly) { eagerlyValidateMethods(service); } // 动态代理,啦啦啦 return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class
[] { service }, new InvocationHandler() { // platform 可以分辨出你是在android,还是java8,又或者别的 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. // 这里的invoke,Object方法都走这里,比如equals、toString、hashCode什么的 if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } // java8默认方法,1.8的新特性 if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } // 这里是核心代码了 ServiceMethod
serviceMethod = (ServiceMethod
) loadServiceMethod(method); OkHttpCall
okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall); } });}复制代码

可以看出创建API使用了动态代理,根据接口动态生成的代理类,将接口的都转发给了负责连接代理类和委托类的InvocationHandler实例,接口方法也都通过其invoke方法来处理。 在invoke方法中,首先会通过Platform.get()方法判断出当前代码的执行环境,之后会先把Object和Java8的默认方法进行一个处理,也是在进行后续处理之前进行去噪。其中的关键代码其实就是最后三句,这也是这篇文章将要分析的 

创建ServiceMethod

erviceMethod
loadServiceMethod(Method method) { // 从缓存里面取出,如果有的话,直接返回好了 ServiceMethod
result = serviceMethodCache.get(method); if (result != null) return result; synchronized (serviceMethodCache) { result = serviceMethodCache.get(method); if (result == null) { // 为null的话,解析方法的注解和返回类型、参数的注解he参数类型,新建一个ServiceMethod result = new ServiceMethod.Builder<>(this, method).build();// -> // 新建的ServiceMethod加到缓存列表里面 serviceMethodCache.put(method, result); } } return result;}复制代码

注解的解析

CallAdapterConverter等到后面再分析,这里先看看parseMethodAnnotation(annotation),功能和其名字一样,其对方法注解进行了解析

/** * 解析方法注解,呜啦啦 * 通过判断注解类型来解析 * @param 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);  }   // 其他的一些方法注解的解析  ...}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;  // value为设置注解方法时候,设置的值,官方例子中的users/{user}/repos or user  if (value.isEmpty()) {    return;  }  // 查询条件的一些判断    ...  this.relativeUrl = value;  this.relativeUrlParamNames = parsePathParameters(value);}`复制代码

在解析注解时,先通过instanceof判断出注解的类型,之后调用parseHttpMethodAndPath方法解析注解参数值,并设置httpMethod、relativeUrl、relativeUrlParamNames等属性。 上面说了API中方法注解的解析,现在来看看方法参数注解的解析,这是通过调用parseParameterAnnotation方法生成ParameterHandler实例来实现的,代码比较多,这里挑选@Query来看看。

else if (annotation instanceof Query) {Query query = (Query) annotation;String name = query.value();boolean encoded = query.encoded();Class
rawParameterType = Utils.getRawType(type);// 返回基础的类gotQuery = true;// 可以迭代,Collectionif (Iterable.class.isAssignableFrom(rawParameterType)) { if (!(type instanceof ParameterizedType)) { throw parameterError(p, rawParameterType.getSimpleName() + " must include generic type (e.g., " + rawParameterType.getSimpleName() + "
)"); } ParameterizedType parameterizedType = (ParameterizedType) type; Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);// 返回基本类型 Converter
converter = retrofit.stringConverter(iterableType, annotations); return new ParameterHandler.Query<>(name, converter, encoded).iterable();} else if (rawParameterType.isArray()) {// Array Class
arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());// 如果是基本类型,自动装箱 Converter
converter = retrofit.stringConverter(arrayComponentType, annotations); return new ParameterHandler.Query<>(name, converter, encoded).array();} else {// Other Converter
converter = retrofit.stringConverter(type, annotations); return new ParameterHandler.Query<>(name, converter, encoded);}复制代码

在@Query中,将分成Collection、array、other三种情况处理参数,之后根据这些参数,调用ParameterHandler中的Query静态类,创建出一个ParameterHandler实例。这样循环直到解析了所有的参数注解,组合成为全局变量parameterHandlers,之后构建请求时会用到 

OkHttpCall

ServiceMethod创建完成之后,我们来看看下一行代码中的OkHttpCall类,里面的包含了请求的执行和响应处理,我们来看看异步请求的做法

OkHttpCall(ServiceMethod
serviceMethod, Object[] args) { this.serviceMethod = serviceMethod; this.args = args;}@Override public void enqueue(final Callback
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();// 创建OkHttp3.Call } catch (Throwable 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
response; try { response = parseResponse(rawResponse);// -> } catch (Throwable e) { callFailure(e); return; } callSuccess(response); } @Override public void onFailure(okhttp3.Call call, IOException e) { try { callback.onFailure(OkHttpCall.this, e); } catch (Throwable t) { t.printStackTrace(); } } private void callFailure(Throwable e) { try { callback.onFailure(OkHttpCall.this, e); } catch (Throwable t) { t.printStackTrace(); } } private void callSuccess(Response
response) { try { callback.onResponse(OkHttpCall.this, response); } catch (Throwable t) { t.printStackTrace(); } }});}private okhttp3.Call createRawCall() throws IOException { Request request = serviceMethod.toRequest(args);// 根据ParameterHandler组装Request.Builder,生成Request okhttp3.Call call = serviceMethod.callFactory.newCall(request);// Retrofit中创建的new OkHttpClient().newCall(request) ... return call;}复制代码

CallAdapter 现在来看看enqueue传入的参数callback,这个参数可能和很多人心中想的并不一样,它并不是用户在使用时传入的那个Callback对象。那么他是从哪里来的呢?不知道你还记不记得我之前在Retrofit.Builder.build()方法中提到过一句代码Platform.get()。在不使用addCallAdapterFactory的情况下。将会使用Platform的一种内部类,在Android环境下将会使用到Android类(这其实是个策略模式)

static class Android extends Platform {  @Override public Executor defaultCallbackExecutor() {    return new MainThreadExecutor();  }  @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {    return new ExecutorCallAdapterFactory(callbackExecutor);  }  static class MainThreadExecutor implements Executor {	// Looper.getMainLooper()就是为嘛响应会在主线程的原因    private final Handler handler = new Handler(Looper.getMainLooper());    @Override public void execute(Runnable r) {      handler.post(r);    }  }}复制代码

上面的代码先稍微放一下,我们继续看retrofit.Bulider.build,其中有几句比较关键的代码

callFactory = new OkHttpClient();callbackExecutor = platform.defaultCallbackExecutor();adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));复制代码

结合Android类中的代码可以看出,其最后生成了ExecutorCallAdapterFactory类。虽然看到了CallAdapter.Factory,但是到底是哪里执行了enqueue方法呢?现在我们来看看retrofit.create的最后一句代码serviceMethod.callAdapter.adapt(okHttpCall) 

Converter

现在回到OkhttpCall.enqueue方法中,在其中还有一句重要的代码没有看,那就是response = parseResponse(rawResponse);,我们来看看这其中做了什么。

Response
parseResponse(okhttp3.Response rawResponse) throws IOException ResponseBody rawBody = rawResponse.body(); // Remove the body's source (the only stateful object) so we can pass th rawResponse = rawResponse.newBuilder() .body(new NoContentResponseBody(rawBody.contentType(), rawBody.conte .build(); ... ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody); try { T body = serviceMethod.toResponse(catchingBody);// 解析body,比如Gson解析 return Response.success(body, rawResponse); } catch (RuntimeException e) { // If the underlying source threw an exception, propagate that rather // a runtime exception. catchingBody.throwIfCaught(); throw e; }}### ServiceMethodR toResponse(ResponseBody body) throws IOException { return responseConverter.convert(body);}复制代码

可以看出parseResponse最终调用了Converter.convert方法。这里以常用的GsonConverterFactory为例。

# GsonConverterFactory@Overridepublic Converter
responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { TypeAdapter
adapter = gson.getAdapter(TypeToken.get(type)); return new GsonResponseBodyConverter<>(gson, adapter);}# GsonResponseBodyConverterfinal class GsonResponseBodyConverter
implements Converter
{ private final Gson gson; private final TypeAdapter
adapter; GsonResponseBodyConverter(Gson gson, TypeAdapter
adapter) { this.gson = gson; this.adapter = adapter; } @Override public T convert(ResponseBody value) throws IOException { JsonReader jsonReader = gson.newJsonReader(value.charStream()); try { return adapter.read(jsonReader); } finally { value.close(); } }}复制代码

responseBodyConverter方法中用到的type参数就是之前我在CallAdapter中提到的responseType方法的返回值。生成adapter方法,用于convert方法使用。OkHttpCall在这之后的代码就比较简单了,通过回调将转换后得响应数据发送出去即可 本文分析了Retrofit的执行流程,其实包含了Retrofit、ServiceMethod、OkHttpCall、CallAdapter、Converter等方面。Retrofit的代码相对是比较少,也比较容易理解的,不过却是很好的架构实例。

Android技术交流群653583088,欢迎大家加入交流,畅谈!本群有免费学习资料视频’

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

你可能感兴趣的文章
思科网络设备模拟器GNS3与SecureCRT关联
查看>>
Activemq判断队列存活脚本(一)
查看>>
系统状态检测 及进程控制
查看>>
xen-tools创建虚拟机找不到root fs的解决办法
查看>>
IBM X3650 M4 服务器
查看>>
备份和归档的区别
查看>>
shell基础
查看>>
linux 文件类型 时间戳 ls bash特性四 文件查看命令 cp move echo
查看>>
如何在XenDesktop中映射USB设备
查看>>
Java并发编程 基础知识学习总结
查看>>
我又发现一个直接就能安装中文小红帽的方法
查看>>
ACM弱校ACMer A HDU1045Fire Net有感
查看>>
cxgrid实现分组统计和添加Footer
查看>>
刘敏华:2013年网络营销行业展望
查看>>
理解MySQL——架构与概念
查看>>
vsftpd虚拟用户
查看>>
ionic 幻灯指令 ion-slide-box
查看>>
发一个经典的Flask-SQLAlchemy使用场景
查看>>
iOS逆向之自动化重签名
查看>>
java 读取pdf、word、Excel文件
查看>>