Retrofit源码分析(一)
2016-06-14 16:52
344 查看
1.基本用法
创建接口public interface GitHubService { @GET("users/{user}/repos") Observable<List<Repo>> listRepos(@Path("user") String user); }
创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build();
生成接口实现类
GitHubService service = retrofit.create(GitHubService.class);
调用接口实现类的请求方法,获取Call对象
Call<List<Repo>> repos = service.listRepos("octocat");
调用Call对象的异步执行方法
repos.enqueue(new Callback<List<Repo>>() { @Override public void onResponse(Response<List<Repo>> response) { // do something } @Override public void onFailure(Throwable t) { // do something } });
2.主线剧情的源码
构造接口通过注解的方式来构造接口,以GET方法为例:
@Documented @Target(METHOD)//注解用于方法 @Retention(RUNTIME)//该注解运行时可用(TIJ P622) public @interface GET { String value() default ""; }
创建Retrofit实例
此处使用了建造者模式,仅以baseurl为例,其余代码省略
public static final class Builder { private Platform platform; private okhttp3.Call.Factory callFactory; private HttpUrl baseUrl; private List<Converter.Factory> converterFactories = new ArrayList<>(); private List<CallAdapter.Factory> adapterFactories = new ArrayList<>(); private Executor callbackExecutor; private boolean validateEagerly; //... 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 Builder baseUrl(HttpUrl baseUrl) { checkNotNull(baseUrl, "baseUrl == null"); List<String> pathSegments = baseUrl.pathSegments(); if (!"".equals(pathSegments.get(pathSegments.size() - 1))) { throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl); } this.baseUrl = baseUrl; return this; } //... }
可以看见有两个List,ConverterFactories和adapterFactories。
在配置它们的时候,使用了保护性拷贝
// 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);
这是后文的重要角色,Retrofit为了解耦合,使用了CallAdapter用来做底层网络请求适配,以及Converter做数据格式的适配。
Converter分为requestConverter、responseConverter和StringConverter三种,可以对请求和响应数据进行包装转换,在定义Retrofit对象时可以自己制定。
在请求时Retrofit使用的是泛型,当把Http Response装换成Java对象时,利用Converter将其转化为请求原本需要的数据类型,完成不用数据格式的适配。
关于Converter我会后续再专门写一篇,也可以阅读以下下面的博客
Retrofit 2.0 源码解析
Retrofit2源码分析(一)
生成接口实现类
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); } }); }
这里就使用了动态代理
动态代理
动态代理机制是Java的一个高级特性, 其主要功能就是可以为委托类对象生成代理类, 代理类可以将所有的方法调用分派到委托对象上反射执行. 动态代理的相关知识可参考 相关的Java书籍.
这里传入newProxyInstance()有三个参数:
接口的classLoader.
只包含接口的class数组.
自定义的InvocationHandler()对象, 该对象实现了invoke() 函数, 通常在该函数中实现对委托类函数的访问.
分析一下上面的代码:
首先做了两个验证,验证接口和标识。
如果我们调用的是来自 Object 类或者平台默认的方法,则会交给方法执行或者平台执行,但从代码上看 isDefaultMethod(method) 直接返回的是 false,可能是为了方便开发者扩展设置各个平台的不同方法调用。
经过两个判断后,会将我们的方法转换成一个 ServiceMethod对象,这是 Retrofit 中最核心的一个类,通过 ServiceMethod 中存储的请求信息向网络方向可以根据 Call 构建出真实的请求实体进行网络请求。
看一下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; }
下面是ServiceBuilder的一部分代码:
static final class Builder<T> { //... public ServiceMethod build() { callAdapter = createCallAdapter(); responseType = callAdapter.responseType(); //... responseConverter = createResponseConverter(); for (Annotation annotation : methodAnnotations) { parseMethodAnnotation(annotation); } if (httpMethod == null) { throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.)."); } //... } //... }
可见,我们通过解析注解的方式来生成ServiceMethod对象,但是解析注解不是效率很低么?
对,这里为了解决效率问题,使用了ServiceMethodCache,利用缓存获取已经解析过注解的ServiceMethod。
最后看看最核心的三行代码:
ServiceMethod serviceMethod = loadServiceMethod(method); OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall);
生成接下来创建了一个 OkHttpCall。
并使用 serviceMethod.CallAdapter 对 OkHttpCall 进行了转化。
这里看起来有点绕,用ServiceMethod生成了OkHttpCall后,还要用serviceMethod去adapt它,形成了一个闭环,其实可以将这里理解为一种跟踪。
而callAdapter是用来做类型转换的,将call对象转换成合适的网络请求方式,默认是OKHttp,这里的设计也是为了方便与RxJava相结合,将OKHttpCall进行一次封装,编程Retrofit的Call,如果你想的话,也可以自己写一个HTTPURLConnectionCall,然后再封装成Retrofit的call。
在 Android 平台,我们使用了 ExecutorCallAdapterFactory 封装网络请求为一个 Call 并将请求结果转发到主线程
Retrofit.create()实际上对 REST 服务接口做了一个动态代理,并返回一个实例,用来完成实际的 http 请求。
获取Call对象
直接按需调用我们声明出来的服务接口实例的自定义方法即可,此时Retrofit会为通过动态代理你生成一个需要的HTTP请求——Call
执行请求
获得Call一般式OKHttpCall
OKHttpCall是用来操作请求的,包括同步执行(execute),异步执行(enqueue),取消请求等功能。Retrofit最新版本默认使用OKHttp去做网络请求。
最后会回调我们定义好的CallBack接口中的\onResponse或者onFailture
到此为止,主线任务上的源码逻辑就理清了。
Retrofit源码1: 为什么写一个interface就可以实现http请求
用 Retrofit 2 简化 HTTP 请求
Retrofit 源码分析之执行流程
相关文章推荐
- 怎样使用pyinstaller打包
- 如何安全退出已调用多个Activity的Application?
- [转] 学习React Native必看的几个开源项目
- Linux 进程及其创建
- Android studio 开发百度地图
- mei yan xiao guo for android
- SGI STL空间配置器-第二级空间配置器
- 自定义EditText(Fly)
- 每日命令:安装新软件
- AFNetWorking之GET,POST,上传图片,下载文件进度监测
- cacti及spine和weathermap插件的配置使用
- C# 对文件与文件夹的操作包括删除、移动与复制
- ORA-00845: MEMORY_TARGET not supported on this system
- iOS应用支持IPV6,就那点事儿
- from 表单get 方式中文乱码
- PNP型三极管是不是要发射极接正电压,基极和集电极接地才能工作?能给张图不?
- PHP扩展开发小记
- 消息响应机制
- scala:函数参数的传名调用(call-by-name)和传值调用(call-by-value)
- 设置theme实现类似于自定义dialog效果