代码示例
不了解Retrofit的人可以先参考这篇介绍文章Retrofit介绍, 文章介绍了 如何通过写一个简单的接口, 就可以实现一个http请求.
本文主要的内容就是介绍retrofit是如何实现的这个功能, 即只实现一个接口 及一些接口api(这些api都被retrofit的注解"修饰"), 没有实现任何具体代码, 就可以完成我们想要的http功能, retrofit到底在后面做了什么?
下面的示例代码是揭开retrofit面纱的入口代码, 通过该代码可以从服务器获取用户列表
//自定义接口public interface Client { @GET("users") Call<List<User>> getUsers();}//使用retrofit来使用自己定义接口实现http请求public class MainActivity { .... public static final String SERVER_URL = "http://10.10.10.10/account"; private OkHttpClient okHttpClient = new OkHttpClient(); private Retrofit.Builder builder = new Retrofit.Builder() .base_url(SERVER_URL) .client(okHttpClient) .addConvertFactory(GsonConvertFactory.create()); Retrofit retrofit = builder.build(); Client client = retrofit.create(Client.class); Call<List<Users>> call = client.getUsers(); List<Users> result = call.execute().body(); ....}
使用builder去创建一个retrofit实例
Retrofit类的源码使用了Builder设计模式, 该类只有一个私有的构造函数, 并且"几乎"没有 任何的setter方法, 这样可以最大程序的保证retrofit对象的"不可变性".
下面是通过builder类可以设置的Retrofit成员变量:
变量名 | 默认值 | note |
---|---|---|
platform | 当前平台 | 当前运行平台:java/android/.. |
callFactory | OkHttpClient | 定义构建Call对象的组件 |
baseUrl | N/A | 服务器基本地址 |
converterFactories | N/A | 对象的序列号/反序列化组件(例如Gson) |
adapterFactories | 该平台的默认adapterFactory | 结果的适配类型(例如RxJava的Observable)(默认为OkHttp的Call类型) |
callbackExecutor | 该平台的executor | 执行实际请求 |
当前运行平台
在上一节可以看到, builder的参数默认是使用了"platform"相关的变量. "platform"在retrofit中代表当前的运行平台, 例如Java8或者Android平台. 代码为与Platform.java. Retrofit的builder类的无参构造函数中, 会调用 Platform.get()
获取当前平台, 对于运行在那个平台的判断, 主要是基于 该平台的一些独特性质, 例如如果系统存在android.os.Build 类, 则代表这是android平台.
private static final Platform PLATFORM = findPlatform();static Platform get() { return PLATFORM;}private static Platform findPlatform() { try { Class.forName("android.os.Build"); if (Build.VERSION.SDK_INT != 0) { return new Android(); } } catch (ClassNotFoundException ignored) { } try { Class.forName("java.util.Optional"); return new Java8(); } catch (ClassNotFoundException ignored) { } try { Class.forName("org.robovm.apple.foundation.NSObject"); return new IOS(); } catch (ClassNotFoundException ignored) { } return new Platform();}
下面是Android平台的实现代码,其重写了父类的两个函数. 另外, 该类还有个 继承了Executor的子类 MainThreadExecutor, 该类包含了android的 UI 线程的handler, 从而保证工作都会在UI线程完成. 至于其重写的函数的意义, 会在后面介绍.
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 { private final Handler handler = new Handler(Looper.getMainLooper()); @Override public void execute(Runnable r) { handler.post(r); } }}
基于自定义interface创建实例
前两部分主要介绍了一下retrofit的builder类, 通过builder的build()函数就可以构造 一个retrofit实例. 接下来就是retrofit很神奇的一步:通过create()函数创建一个自定义 接口的对象实例:
Client client = retrofit.create(Client.class);
在前面的示例代码中, Client是我们创建的一个接口, 并没有任何的"实体"代码(实现类), 但是这里通过create()函数就可以生成一个"有血有肉"的对象实例. 通过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, 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); } });}
前面的"if"语句暂时先不管, 最后的 "return" 返回了一个 Proxy.newProxyInstance()
函数的结果, 这里使用到了java的 动态代理 的编程技巧. 通过该函数, 我们可以拿到一个 前面自定义的 "Client" 的一个 代理类, 其功能就相当于一个Client对象, 即我们可以通过 它调用Client里的各个成员函数.
这里最重要的是其第三个参数, 该参数是一个匿名的 InvocationHandler(), 该类的意义在于: 当我们通过代理类调用成员函数时, 最后调用的其实是该匿名类的 invoke()函数, 该函数的参数method就是Client类的方法, 参数就是Client类的参数. 这就是我们可以通过retrofit实现一个interface实例的核心代码. 至于怎么通过调用 具体的函数实现实际的http请求, 则需要看一下 invoke() 的具体实现. 在该函数的实现里, 最重要的就是这几行.
ServiceMethod serviceMethod = loadServiceMethod(method);OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);return serviceMethod.callAdapter.adapt(okHttpCall);
通过invoke()执行函数, 返回一个Call
函数的封装类:ServiceMethod
在上一节的最后,提到了具体自定义service 的成员函数调用与invoke()函数 的后几行有关, 这里先看一下第一行代码 ServiceMethod serviceMethod = loadServiceMethod(method);
.
ServiceMethod类是函数的封装类, 它保存了我们在接口中定义的函数的所有信息(前面例子中我们定义了函数 getUsers() ), 包括如下内容:
内容 | note |
---|---|
请求方法 | 如POST/GET |
请求体 | |
请求的url | base + 相对地址 |
请求头部 | |
函数参数 | 参数也会被"注解"修饰 |
函数返回值 | 一般为Call |
other | 一些具体的http协议相关的内容, 例如是否为multipart, form等 |
当调用 loadServiceMethod()函数时, 实际就是基于处理这个函数的所有信息, 这些信息是可以通过java的Method类拿到的.
对于ServiceMethod类的具体处理过程, 会在下一篇文章讲述.
执行函数, 获取返回值(Call)
在invoke()函数的最后两行, 首先基于通过分析函数生成的ServiceMethod实例来 创建一个OkHttpClient对象, 然后调用代码 return serviceMethod.callAdapter.adapt(okHttpCall);
来完成"代理"的作用, 这个invoke()的返回值"等同于"我们调用自定义函数的返回值. invoke()的返回值总是Object类型, 将其转换为自定义函数的返回值类型即可. 一般这个返回值都为Call类型.
这里主要看一下最后一行代码. 这行代码可以分成两部分讲解:
serviceMethod的callAdapter变量.
callAdapter变量的adapt()函数
ServiceMethod的callAdapter变量
CallAdapter是Call的适配器类, 在将一个自定义函数解析成ServiceMethod实例时, 会生成这个ServiceMethod的callAdapter变量. 下面的代码展示了创建过程.
//SeviceMethod.java private CallAdapter<?> createCallAdapter() { Type returnType = method.getGenericReturnType(); if (Utils.hasUnresolvableType(returnType)) { throw methodError( "Method return type must not include a type variable or wildcard: %s", returnType); } if (returnType == void.class) { throw methodError("Service methods cannot return void."); } Annotation[] annotations = method.getAnnotations(); try { return retrofit.callAdapter(returnType, annotations); } catch (RuntimeException e) { // Wide exception range because factories are user code. throw methodError(e, "Unable to create call adapter for %s", returnType); } }//Retrofit.java public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) { checkNotNull(returnType, "returnType == null"); checkNotNull(annotations, "annotations == null"); int start = adapterFactories.indexOf(skipPast) + 1; for (int i = start, count = adapterFactories.size(); i < count; i++) { CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this); if (adapter != null) { return adapter; } }
上面的代码是callAdapter变量的创建过程, 第一个函数 createCallAdapter()
首先 获取了函数的"返回类型"和"注解", 并基于这两个内容调用Retrofit的 callAdapter()
函数, 并最终调用了 nextCallAdapter()
函数. 后者会检查retrofit的adapterFactories 变量中是否包含能够匹配这个返回值类型和注解的CallAdapter, 并返回. 那么问题来了: 这个adapterFactories中到底有没有匹配能够匹配返回类型和注解的CallAdapter呢? 这就要看一下这个 factory 的具体实现过程.
Retrofit的adapterFactories的真实面目
注:这里只解释了Android平台的情况.
adapterFactories变量是在retrofit的builder中初始化的, builder提供了一个 addCallAdapterFactory()
函数可以让使用者向factories添加自定义CallAdapter, 同时 , 在最后的build()阶段,会将该当前运行平台的默认CallAdapterFactory 添加到fatories里. 这里假设我们没有添加任何自定义CallAdapter, 那么factories里只有平台的默认CallAdapterFacotry了.
对于Android 平台来说, 这个"默认"的CallAdapterFactory代码如下, 该函数返回一个ExecutorCallAdapterFactory实例.
//Android platform @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) { return new ExecutorCallAdapterFactory(callbackExecutor); }
根据前面的内容,当对该实例调用 get()
函数时, 如何返回一个 可以适配"返回类型"和"函数注解"的CallAdapter实例, 这就要看下 ExecutorCallAdapterFactory 的具体实现, 其代码如下. 从代码可以看出, 对于任何自定义函数, 只要其返回类型为"Call"类, 那么都会生成一个匿名的"CallAdapter"实例.该实例实现了 adapter()
方法, 使其可以返回一个具体的Call的子类, 即 ExecutorCallbackCall().
@Overridepublic CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { if (getRawType(returnType) != Call.class) { return null; } final Type responseType = Utils.getCallResponseType(returnType); return new CallAdapter<Call<?>>() { @Override public Type responseType() { return responseType; } @Override public <R> Call<R> adapt(Call<R> call) { return new ExecutorCallbackCall<>(callbackExecutor, call); } };}
以上就是调用自定义interface的具体某个函数的过程, 以Android平台为例, 通过调用函数, 最终会获得一个ExecutorCallbackCall实例. 通过这个Call实例, 我们就可以实现具体的Http请求.
执行具体的Http请求
通过前面的内容, 已经知道调用函数可以获得一个ExecutorCallbackCall实例, 那么就可以通过执行该实例的execute()或enqueue()函数执行具体的http请求了. 这一部分是OkHttp相关的内容, 会在后面文章陆续说明.
//具体请求代码List<Users> result = call.execute().body();
这里想补充一下ExecutorCallbackCall类的一个变量: callbackExecutor.
在Retrofit的设计中,通过Call进行http请求有两种方法: execute()和enqueue(). 前者是同步请求, 后者是异步请求. 对于异步请求, 需要传递一个callback参数进行 回调, 处理返回结果. 在ExecutorCallbackCall中, 回调的具体处理过程就是通过 变量 callbackExecutor 完成的. 因为ExecutorCallbackCall对应的是Android平台, 所以我们来看一下这个 callbackExecutor 有什么特殊之处. 下面的代码追溯了这个变量的最终出处.
//Android平台通过该函数创建factory @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) { return new ExecutorCallAdapterFactory(callbackExecutor); }//Retrofit在builder中调用了上面的函数, 并传入了executor参数adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));//builder的callbackExecutor的创建, 调用了平台的相关函数callbackExecutor = platform.defaultCallbackExecutor();//平台相关函数的实现 @Override public Executor defaultCallbackExecutor() { return new MainThreadExecutor(); } static class MainThreadExecutor implements Executor { private final Handler handler = new Handler(Looper.getMainLooper()); @Override public void execute(Runnable r) { handler.post(r); } }
通过上面代码可以看出, Android平台的这个executor其实是一个带有UI线程handler的 executor, 所以最后执行execute时, 会将runnable传给UI线程执行. 即,当调用enqueue() 函数时, 回调是在UI线程中执行的.
over.
附注:
动态代理介绍
动态代理机制是Java的一个高级特性, 其主要功能就是可以为委托类对象生成代理类, 代理类可以将所有的方法调用分派到委托对象上反射执行. 动态代理的相关知识可参考 相关的Java书籍. 这里传入newProxyInstance()有三个参数: 1, 接口的classLoader. 2, 只包含接口的class数组. 3, 自定义的InvocationHandler()对象, 该对象实现了invoke() 函数, 通常在该函数中实现对委托类函数的访问. 所以从create函数可以看出, *其实该函数 返回的是一个动态代理类对象(被转化成了我们自定义的接口), 当我们调用该接口的自定义 函数时, 我们调用的实际是invoke()函数.* 而要执行的方法被当作参数传给了invoke.