Retrofit2 笔记
来源:互联网 发布:mmd动作数据怎么做 编辑:程序博客网 时间:2024/06/05 08:01
Retrofit是针对于Android/Java的、基于okHttp的、一种轻量级且安全的、并使用注解方式的网络请求框架。
Retrofit的设计非常插件化而且轻量级,真的是非常高内聚而且低耦合
Retrofit的优势
首先,Retrofit使用注解方式,大大简化了我们的URL拼写形式,而且注解含义一目了然,简单易懂;
其次,Retrofit使用简单,结构层次分明,每一步都能清晰的表达出之所以要使用的寓意;
再者,Retrofit支持同步和异步执行,使得请求变得异常简单,只要调用enqueue/execute即可完成;
最后,Retrofit更大自由度的支持我们自定义的业务逻辑,如自定义Converters。
从 Retrofit 的创建方法可以看出,使用的是 Builder 模式
Retrofit 中有如下的几个关键变量:(取自:https://segmentfault.com/a/1190000006767113)
//用于缓存解析出来的方法 private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>(); //请求网络的OKHttp的工厂,默认是 OkHttpClient private final okhttp3.Call.Factory callFactory; //baseurl private final HttpUrl baseUrl; //请求网络得到的response的转换器的集合 默认会加入 BuiltInConverters private final List<Converter.Factory> converterFactories; //把Call对象转换成其它类型 private final List<CallAdapter.Factory> adapterFactories; //用于执行回调 Android中默认是 MainThreadExecutor private final Executor callbackExecutor; //是否需要立即解析接口中的方法 private final boolean validateEagerly;
再看一下Retrofit 中的内部类 Builder 的 builder 方法:
public Retrofit build() { if (baseUrl == null) { throw new IllegalStateException("Base URL required."); } okhttp3.Call.Factory callFactory = this.callFactory; if (callFactory == null) { //默认创建一个 OkHttpClient callFactory = new OkHttpClient(); } Executor callbackExecutor = this.callbackExecutor; if (callbackExecutor == null) { //Android 中返回的是 MainThreadExecutor 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);}
loadServiceMethod: 先到缓存中找,缓存中没有再去创建。这里创建了 ServiceMethod 对象。ServiceMethod 用于把接口方法的调用转换成一个 HTTP 请求。其实,在 ServiceMethod 中,会解析接口中方法的注解、参数等,它还有个 toRequest 方法,用于生成一个 Request 对象。这个 Request 对象就是 OkHttp 中的 Request,代表了一条网络请求(Retrofit 实际上把真正请求网络的操作交给了 OkHttp 执行)。
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;}
OkHttpCall继承于 Call 接口。Call 是Retrofit 的基础接口,代表发送网络请求与响应调用,它包含下面几个接口方法:
Response<T> execute() throws IOException; //同步执行请求void enqueue(Callback<T> callback); //异步执行请求,callback 用于回调boolean isExecuted(); //是否执行过void cancel(); //取消请求boolean isCanceled(); //是否取消了Call<T> clone(); //克隆一条请求Request request(); //获取原始的request
OkHttpCall 是 Call 的一个实现类,它里面封装了 OkHttp 中的原生 Call,在这个类里面实现了 execute 以及 enqueue 等方法,其实是调用了 OkHttp 中原生 Call 的对应方法。
接下来把 OkHttpCall 传给 serviceMethod.callAdapter 对象,
那么 CallAdapter 是干嘛的呢?上面调用了adapt 方法,它是为了把一个 Call 转换成另一种类型,比如当 Retrofit 和 RxJava 结合使用的时候,接口中方法可以返回 Observable,这里相当于适配器模式。默认情况下得到的是一个 Call 对象,它是ExecutorCallbackCall,
static final class ExecutorCallbackCall<T> implements Call<T> {final Executor callbackExecutor;final Call<T> delegate;ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) { this.callbackExecutor = callbackExecutor; this.delegate = delegate;}@Override public void enqueue(final Callback<T> callback) { if (callback == null) throw new NullPointerException("callback == null"); delegate.enqueue(new Callback<T>() { @Override public void onResponse(Call<T> call, final Response<T> response) { callbackExecutor.execute(new Runnable() { @Override public void run() { if (delegate.isCanceled()) { // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation. callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled")); } else { callback.onResponse(ExecutorCallbackCall.this, response); } } }); } @Override public void onFailure(Call<T> call, final Throwable t) { callbackExecutor.execute(new Runnable() { @Override public void run() { callback.onFailure(ExecutorCallbackCall.this, t); } }); } });}
在 enqueue 方法中,调用了 OkHttpCall 的 enqueue,所以这里相当于静态的代理模式。OkHttpCall 中的 enqueue 其实又调用了原生的 OkHttp 中的 enqueue,这里才真正发出了网络请求,部分代码如下:
@Override public void enqueue(final Callback<T> callback) { if (callback == null) throw new NullPointerException("callback == null"); //真正请求网络的 call okhttp3.Call call; Throwable failure; synchronized (this) { if (executed) throw new IllegalStateException("Already executed."); executed = true; //省略了部分发代码 ... call = rawCall; //enqueue 异步执行 call.enqueue(new okhttp3.Callback() { @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) throws IOException { Response<T> response; try { //解析数据 会用到 conveterFactory,把 response 转换为对应 Java 类型 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<T> response) { try { callback.onResponse(OkHttpCall.this, response); } catch (Throwable t) { t.printStackTrace(); } } });}
OkHttp 获取数据后,解析数据并回调callback响应的方法,一次网络请求便完成了。
Retrofit对象create方法返回Proxy.newProxyInstance动态代理
Java动态代理就是给了程序员一种可能:当你要调用某个Class的方法前或后,插入你想要执行的代码:
比如你要执行某个操作前,你必须要判断这个用户是否登录,或者你在付款前,你需要判断这个人的账户中存在这么多钱(见http://www.jianshu.com/p/c1a3a881a144描述)。
/** Create an implementation of the API defined by the {@code service} interface. */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.如果是Object中的方法,直接调用 if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } //为了兼容 Java8 平台,Android 中不会执行 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); }});//取自:https://segmentfault.com/a/1190000006767113
为什么要使用动态代理?
Call<ZhuanLanAuthor> call = api.getAuthor("qinchao");
create方法返回的api对象其实是一个动态代理对象,并不是一个真正的Api接口的implements产生的对象,当api对象调用getAuthor方法时会被动态代理拦截,然后调用Proxy.newProxyInstance方法中的InvocationHandler对象,它的invoke方法会传入3个参数:
- Object proxy: 代理对象,不关心这个
- Method method:调用的方法,就是getAuthor方法
- Object…args:方法的参数,就是”qinchao”
而Retrofit关心的就是method和它的参数args,接下去Retrofit就会用Java反射获取到getAuthor方法的注解信息,配合args参数,创建一个ServiceMethod对象
ServiceMethod就像是一个中央处理器,传入Retrofit对象和Method对象,调用各个接口和解析器,最终生成一个Request,包含api 的域名、path、http请求方法、请求头、是否有body、是否是multipart等等。最后返回一个Call对象,Retrofit2中Call接口的默认实现是OkHttpCall,它默认使用OkHttp3作为底层http请求client.
使用Java动态代理的目的就要拦截被调用的Java方法,然后解析这个Java方法的注解,最后生成Request由OkHttp发送
OkHttpCall就是调用ServiceMethod获得一个可以执行的Request对象,然后等到Http请求返回后,再将response body传入ServiceMethod中,ServiceMethod就可以调用Converter接口将response body转成一个Java对象.
Retrofit会对解析过的请求进行缓存,就在Map
Retrofit的接口设计及其4个接口
Retrofit的设计非常插件化而且轻量级,真的是非常高内聚而且低耦合,这个和它的接口设计有关。Retrofit中定义了4个接口:
Callback<T> 请求数据返回的接口Converter<F, T> 将HTTP返回的数据解析成Java对象Call<T> 发送一个HTTP请求,默认的实现是OkHttpCall<T>CallAdapter<T> 将Call对象转换成另一个对象,属性只有responseType一个,还有一个<R> T adapt(Call<R> call)方法,这个接口的实现类也只有一个DefaultCallAdapter
Call是Retrofit中重要的一个概念,代表被封装成单个请求/响应的交互行为
通过调用Retrofit2的execute(同步)或者enqueue(异步)方法,发送请求到网络服务器,并返回一个响应(Response)。
- 独立的请求和响应模块
- 从响应处理分离出请求创建
- 每个实例只能使用一次。
- Call可以被克隆。
- 支持同步和异步方法。
- 能够被取消。
好的代码就是依赖接口而不是实现
Retrofit 整个框架的代码不算太多,还是比较易读的。主要就是通过动态代理的方式把 Java 接口中的注解形式请求解析为响应的网络请求,然后交给 OkHttp 去执行。并且可以适配不同的 CallAdapter,可以方便与 RxJava 结合使用。
Retrofit非常巧妙的用注解来描述一个HTTP请求,将一个HTTP请求抽象成一个Java接口,然后用了Java动态代理的方式,动态的将这个接口的注解“翻译”成一个HTTP请求,最后再执行这个HTTP请求
Retrofit的功能非常多的依赖Java反射,代码中其实还有很多细节,比如异常的捕获、抛出和处理,大量的Factory设计模式
Retrofit中接口设计的恰到好处,在你创建Retrofit对象时,让你有更多更灵活的方式去处理你的需求,比如使用不同的Converter、使用不同的CallAdapter,这也就提供了你使用RxJava来调用Retrofit的可能
什么是好的代码?像Picasso和Retrofit这样的就是好的代码,扩展性强、低耦合、插件化
Retrofit源码的组成:
- 一个retrofit2.http包,里面全部是定义HTTP请求的注解,比如GET、POST、PUT、DELETE、Headers、Path、Query等等
- 余下的retrofit2包中十几个类和接口就是全部retrofit的代码了,代码真的很少,很简单,因为retrofit把网络请求这部分功能全部交给了okHttp了
以下描述粘贴自:
Retrofit2使用简介,Rabtman,7月 16, 2016
固定参数查询
@GET("tasks?pageNo=1")Call<PageDataDTO<HistoryTaskDTO>> getHistoryTasks();// 方法调用//service.getHistoryTasks();// 请求头// GET http://.../tasks?pageNo=1 HTTP/1.1
动态参数(Query)
@GET("tasks")Call<PageDataDTO<HistoryTaskDTO>> getHistoryTasks(@Query("pageNo") int no);// 方法调用//service.getHistoryTasks(2);// 请求头// GET http://.../tasks?pageNo=2 HTTP/1.1
动态参数(QueryMap)
@GET("tasks")Call<PageDataDTO<HistoryTaskDTO>> getHistoryTasks(@QueryMap Map<String,String> map);//方法调用HashMap<String,String> params = new HashMap<>();params.put("pageNo", "1");params.put("pageSize", "10");service.getHistoryTasks(params);// 请求头// GET http://.../tasks?pageNo=1&pageSize=10 HTTP/1.1
固定头
@GET("tasks")@Headers("Accept-Encoding: application/json")Call<PageDataDTO<HistoryTaskDTO>> getHistoryTask();// 方法调用//service.getHistoryTask();// 请求头// GET http://.../tasks HTTP/1.1// Accept-Encoding: application/json
动态头
@GET("tasks")Call<PageDataDTO<HistoryTaskDTO>> getHistoryTask(@Header("Location") String location);// 方法调用//service.getHistoryTask("china");// 请求头// GET http://.../tasks HTTP/1.1// Location: china
路径替换
@GET("{token}/account/total/fetch")Call<ResponseBody<String>> getTotal(@Path("token") String token);//方法调用//service.getTotal("fd88b2ff");// 请求头// GET http://.../fd88b2ff/account/total/fetch HTTP/1.1POST请求(无BODY)//POST请求无BODY@POST("auth")Call<UserDTO> userLogin();
POST请求(带BODY,)
//POST请求带BODY,其中的body对象将被Retrofit实例指定的转换器转换@POST("auth")Call<UserDTO> userLogin(@Body User user);POST请求(表单传参,Field)@FormUrlEncoded@POST("auth")Call<UserDTO> userLogin( @Field("username") String username);
POST请求(表单传参,FieldMap)
@FormUrlEncoded@POST("auth")Call<UserDTO> userLogin(@FieldMap Map<String, String> params);//方法调用HashMap<String,String> params = new HashMap<>(); params.put("username", "15222222222"); params.put("code", "123456");service.userLogin(params);
动态URL
@FormUrlEncoded Call<UserDTO> getUserInfo(@Url String url);
参考链接:
Android 网络框架之Retrofit2使用详解及从源码中解析原理
What is the best library to make HTTP calls from Java/Android?
Comparison of Android networking libraries: OkHTTP, Retrofit, and Volley [closed]
Retrofit 2.0: The biggest update yet on the best HTTP Client Library for Android
再回头看,有如下几篇源码解析的好文章,list如下:
Retrofit2 源码解析 白瓦力 2015.12.13
Android Retrofit源码解析 segmentfault 然则 2016年08月30日发布
Retrofit2 源码解析 Dec 13 2015
Android 网络框架之Retrofit2使用详解及从源码中解析原理
使用Retrofit2.0+OkHttp3.0实现缓存处理, 2016-7-29
Android网络框架源码分析二—Retrofit,2016-1-3(包含与Volley对比)
开源项目分析android-open-project-analysis,包含流程图和类图
拆轮子系列:拆 Retrofit Posted by Piasy on June 25, 2016
- Retrofit2 笔记
- Retrofit2 笔记
- Retrofit2的学习笔记
- Retrofit2.1.0 学习笔记
- retrofit2学习笔记
- Retrofit2学习笔记-1
- retrofit2.3.0 学习笔记
- Android Retrofit2.0 学习笔记
- Retrofit2 学习笔记(一)
- Retrofit2 学习笔记(二)
- Retrofit2+RxJava+Okhttp学习笔记
- Retrofit2
- retrofit2
- Retrofit2
- retrofit2.0 使用笔记-文件上传功能
- Android Volley & Retrofit2 & Http基础知识 笔记
- Android学习笔记之Retrofit2的使用
- android学习笔记——Retrofit2.0学习
- 做到这几点,才能吸引HR的注意!
- SAP批次级别的意义及启用操作
- Qt基础 04_Qt对话框(三) 标准对话框
- 在wicket框架下生成年份与月份DropDownChoice下拉框
- C++:内联函数
- Retrofit2 笔记
- 欢迎使用CSDN-markdown编辑器
- AV1视频编码标准资源汇总
- PHP学习笔记
- jquery怎么讲html页面中指定标签,删除,即从html代码中删除,包括本元素、及其子元素
- 数据应用达人之SQL基础教程分享1
- Apache自带ab压测功能结果说明
- CentOS7中,vnc分辨率设置。
- Thinkphp5使用阿里大于短信验证