本文主要是介绍Retrofit2 笔记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
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的工厂,默认是 OkHttpClientprivate final okhttp3.Call.Factory callFactory;//baseurlprivate final HttpUrl baseUrl;//请求网络得到的response的转换器的集合 默认会加入 BuiltInConverters private final List<Converter.Factory> converterFactories;//把Call对象转换成其它类型private final List<CallAdapter.Factory> adapterFactories;//用于执行回调 Android中默认是 MainThreadExecutorprivate 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) {//默认创建一个 OkHttpClientcallFactory = new OkHttpClient();}Executor callbackExecutor = this.callbackExecutor;if (callbackExecutor == null) {//Android 中返回的是 MainThreadExecutorcallbackExecutor = 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");//真正请求网络的 callokhttp3.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.1
POST请求(无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 笔记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!