Retrofit2 源码分析
来源:互联网 发布:淘宝的投诉电话是多少 编辑:程序博客网 时间:2024/06/18 08:50
Retrofit 提供了一种REST格式的网络请求,对OKhttp进行了更近一层的封装,最近阅读了一下源码,Retrofit本身代码量不多,相当紧凑,值得学习
Retrofit最核心的类为Retrofit.java和ServiceMethod.java这两个类,其中Retrofit.Builder这个内部类负责Retrofit模块的初始化工作。
在设计项目的时候在需要Retrofit的时候通过改变这个Builder来创建不同的Retrofit实例.
来看看这个Builder最后构建Retrofit的时候初始化了哪些
/** * Create the {@link Retrofit} instance using the configured values. * <p> * Note: If neither {@link #client} nor {@link #callFactory} is called a default {@link * OkHttpClient} will be created and used. */ public Retrofit build() { if (baseUrl == null) { throw new IllegalStateException("Base URL required."); } okhttp3.Call.Factory callFactory = this.callFactory; if (callFactory == null) { callFactory = new OkHttpClient(); } Executor callbackExecutor = this.callbackExecutor; if (callbackExecutor == null) { callbackExecutor = platform.defaultCallbackExecutor(); } // Make a defensive copy of the adapters and add the default Call adapter. List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); // Make a defensive copy of the converters. List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories); return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories, callbackExecutor, validateEagerly); }}
可以看到初始化了OkhttpClient,默认的adapter,还有baseUrl(基础的连接地址),还有转换的工厂列表,适配器工厂列表,validateEagerly这个变量是是否先缓存Service中的方法
接下来看这个函数
// Single-interface proxy creation guarded by parameter safety.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, 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); } });}这个函数我们在创建具体的服务API接口的时候会调用
Utils.validateServiceInterface(service);这句话判断这个Service是否是接口
if (validateEagerly) { eagerlyValidateMethods(service); }这个是缓存Service的Method,可以具体看下:
private void eagerlyValidateMethods(Class<?> service) { Platform platform = Platform.get(); for (Method method : service.getDeclaredMethods()) { if (!platform.isDefaultMethod(method)) { loadServiceMethod(method); } }}这个地方可以看出这个方法将Service中的所有API接口都先load好,做一个缓存的作用,这样在真正调用方法的时候就可以直接从缓存中拿,提升了效率。
接下来就是用的反射了,Proxy这个类是属于Java Reflect包下面的,用反射的方法来构造接口类Service的实例,
当我们来调用Service中的某个具体接口的时候,会回调
InvocationHandler中的invoke方法。来看看这里具体做了哪些操作:
if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); }这里是来判断这个方法是否是Object这个基类的方法,如果是则直接返回Object的方法。这里可以看出代码逻辑的严谨性是值得我们来学习的。
if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args);}这里判断条件始终为false,所以不会走。保留这个逻辑应该是想针对某些平台的默认方法来做的。
接着
ServiceMethod serviceMethod = loadServiceMethod(method);这就是最核心的方法了。用来加载API的具体方法
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);初始化了一个OkhttpCall来处理网络接连,这里Retrofit对Okhttp请求进行了一次封装
最后调用
serviceMethod.callAdapter.adapt(okHttpCall)
来通过各种adapter来处理请求,并且返回相应格式的数据。下面我们来看下loadServiceMethod这个方法
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;}这里做了一个缓存,最核心的是这句:
result = new ServiceMethod.Builder(this, method).build();来看看ServiceMethod做了些什么
首先是构建了一个Builder:
public Builder(Retrofit retrofit, Method method) { this.retrofit = retrofit; this.method = method; this.methodAnnotations = method.getAnnotations(); this.parameterTypes = method.getGenericParameterTypes(); this.parameterAnnotationsArray = method.getParameterAnnotations();}这个Builder其实主要的就是获取方法的注解,参数类型以及参数上的注解,做了一些初始化的工作
来看看Builder.build()干了些啥
public ServiceMethod build() { 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 = createResponseConverter(); for (Annotation annotation : methodAnnotations) { parseMethodAnnotation(annotation); } if (httpMethod == null) { throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.)."); } 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);}首先:
callAdapter = createCallAdapter();这句话具体干了一件事:根据Method的返回值来获取Adapter列表中对应的Adapter。
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?");}这里是做一个保护,Retrofit是不允许直接返回Response的。
responseConverter = createResponseConverter();这句话来获取返回数据的转换实例。
接下来的代码比较简单,根据注解来解析对应的参数,最后返回ServiceMethod实例。
整个代码逻辑十分清晰,对于具体的Adapter和Converter,Retrofit做了拆分,剥离主项目,想必是想让Retrofit更加聚焦于框架的设计,
而具体的业务类似返回数据解析和返回数据处理都用接口的方式来处理,使得整个项目架构清晰。
这种框架的思想是值得我们学习的,看看代码其实逻辑并不复杂。但是如果我们来写可能就会把Adapter和Converer写到框架中去了。
所以我们在平时写代码的时候更多的需要去来抽象一些模型,使得我们在框架设计上有更多的积累。
- Retrofit2.0源码分析
- Retrofit2源码分析
- Retrofit2 源码分析
- Retrofit2 源码分析
- Retrofit2.0源码分析
- Retrofit2 源码分析
- retrofit2源码分析
- Retrofit2源码分析
- retrofit2源码分析
- retrofit2.0源码分析
- Retrofit2实现源码分析
- Retrofit2源码分析
- Retrofit2 源码分析(清晰版)
- Retrofit2源码初探
- Retrofit2源码解读
- Retrofit2源码解析
- Retrofit2 源码解析
- Retrofit2.0源码解析
- 线段树
- 【USACO】洛谷1218 特殊的质数肋骨 Superprime Rib
- Spring AOP之AspectJ的注解方式使用
- c语言调用shell命令
- 安卓中本地服务Service和远程服务AIDL的使用
- Retrofit2 源码分析
- Hibernate中的Session
- 126. Word Ladder II
- 第二章学习总结
- lua学习(4)_____表达式、语句
- Android ViewDragHelper简单使用
- Vim学习笔记
- C#设计模式学习小结之七 建造者模式
- 若FPGA开发板没有AS端口,如何永久保存程序?