简单研究下Retrofit
2015-09-24 15:36:26
第一部分:
1. 什么是Retrofit?
(点击图片有惊喜)
以上是来自官网的解释,言简意赅,咳咳,我就不翻译了~
2. 如何使用Retrofit?
2.1 具体的请大家参考官网的配置或者下载jar包~
不过当前好像v2.0.0.1-Beta jar,改动很大(删除了RestAdapter,真不知道他们怎么考虑向后兼容的问题),而且还是beta版,所以我的研究都是基于1.9.0,这个版本实际使用的也比较多。需要的同学可自行下载相关的jar包和源码包,列表如下:
涉及到的jar包比较多,比如okhttp、rxjava、rxandroid等,由于Retrofit支持RxJava和RxAndroid,而且用到了OkHttp,所以一并打包。至于RxJava的功能和使用,有时间再研究吧,挺好的想法。
2.2 如何使用Retrofit?
第一步:创建一个interface,Retrofit将HTTP请求转换成一个interface
public interface ApiManagerService {
@GET("/weather")
Observable<WeatherData> getWeather(@Query("q") String place, @Query("units") String units); @GET("/weather")
WeatherData getWeatherBody(@Query("q") String place, @Query("units") String units); @GET("/weather")
void getWeatherCallback(@Query("q") String place, @Query("units") String units, Callback<WeatherData> callback); @FormUrlEncoded
@POST("/weather")
WeatherData getWeatherField(@Field("name") String name, @Field("id") String id);
}
第二步:创建一个ApiManagerService的Proxy对象,同时设置请求的url前部,之所以叫“前部”,是因为这里设置的“http://api.openweathermap.org/data/2.5”+“/weather”才能形成一个完整的请求URL。此处的URL是一个国外的提供天气的URL。
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("http://api.openweathermap.org/data/2.5")
.setClient(new MyClient(new OkClient()))
.build(); final ApiManagerService apiManager = restAdapter.create(ApiManagerService.class);
第三步:调用。注意此调用是一个同步调用,在当前线程,如果是Android,那就是在UI线程,至于原因,我们后面解析。
WeatherData data = apiManager.getWeatherBody("Budapest,hu", "metric");
WeatherData.java是一个用来解析json的Object,部分代码如下:
public class WeatherData {
public String base;
public long id;
public long dt;
public String name;
public int cod;
}
很简单吧,我们很轻松的搞定了一个GET网络请求,并且了返回了一个我们自己想要的Object,而不是一大串的Json~
2.3 Retrofit封装了多种网络请求和请求形式,比如:GET
, POST
, PUT
, DELETE,和各种丰富的参数形式,如:@Path、@Query、@QueryMap、@Field等。这些都比较简单,大家参考一下官网的介绍就好了。
第二部分:
大家是不是觉得特别神奇,反正我刚接触到这玩意的时候觉得挺神奇的~下面我们就来一点一点的扒开她的神秘面纱~
1. 先从RestAdapter入手,大家可以发现RestAdapter是个关键。首先看看RestAdapter是怎么创建的,代码如下:
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("http://api.openweathermap.org/data/2.5")
.setClient(new MyClient(new OkClient()))
.build();
使用了Builder,可见创建RestAdapter对象需要的参数都是可选的,事实证明确实是这样,看源码:
private RestAdapter(Endpoint server, Provider clientProvider, Executor httpExecutor, Executor callbackExecutor, RequestInterceptor requestInterceptor, Converter converter, Profiler profiler, ErrorHandler errorHandler, RestAdapter.Log log, RestAdapter.LogLevel logLevel) {
this.serviceMethodInfoCache = new LinkedHashMap();
this.server = server;
this.clientProvider = clientProvider;
this.httpExecutor = httpExecutor;
this.callbackExecutor = callbackExecutor;
this.requestInterceptor = requestInterceptor;
this.converter = converter;
this.profiler = profiler;
this.errorHandler = errorHandler;
this.log = log;
this.logLevel = logLevel;
}
这是RestAdapter的私有构造器~再看看RestAdapter.Builder
public RestAdapter build() {
if(this.endpoint == null) {
throw new IllegalArgumentException("Endpoint may not be null.");
} else {
this.ensureSaneDefaults();
return new RestAdapter(this.endpoint, this.clientProvider, this.httpExecutor, this.callbackExecutor, this.requestInterceptor, this.converter, this.profiler, this.errorHandler, this.log, this.logLevel, null);
}
} private void ensureSaneDefaults() {
if(this.converter == null) {
this.converter = Platform.get().defaultConverter(); // 1
} if(this.clientProvider == null) {
this.clientProvider = Platform.get().defaultClient(); // 2
} if(this.httpExecutor == null) {
this.httpExecutor = Platform.get().defaultHttpExecutor(); // 3
} if(this.callbackExecutor == null) {
this.callbackExecutor = Platform.get().defaultCallbackExecutor(); // 4
} if(this.errorHandler == null) {
this.errorHandler = ErrorHandler.DEFAULT; // 5
} if(this.log == null) {
this.log = Platform.get().defaultLog(); // 6
} if(this.requestInterceptor == null) {
this.requestInterceptor = RequestInterceptor.NONE; // 7
} }
我只贴出来了关键部分的代码,至于那些set***()就没必要了吧~看红色部分代码,ensureSaneDefaults()方法确保每一个RestAdapter需要的参数都是初始化了的,所以创建RestAdapter的时候你可以不用set任何参数。下面我们逐个看看初始化的参数。
首先说明一下,Retrofit在初始化这些参数的时候是区别对待Android和Base(Java)的,不同的platform有不同的实现。
//1 初始化converter,这个就是帮你解析json的转换器,在Android和Base都使用的是自己定义的GsonConverter,其中使用了Google的Gson。
//2 初始化clientProvider,就是执行http请求的HttpClient,Android平台如下
Provider defaultClient() {
final Object client;
if(Platform.hasOkHttpOnClasspath()) {
client = Platform.OkClientInstantiator.instantiate();
} else if(VERSION.SDK_INT < 9) {
client = new AndroidApacheClient();
} else {
client = new UrlConnectionClient();
} return new Provider() {
public Client get() {
return (Client)client;
}
};
}
在这里考虑到了Android平台API Level 9前后的区别,赞一个~
Base平台如下:
Provider defaultClient() {
final Object client;
if(Platform.hasOkHttpOnClasspath()) {
client = Platform.OkClientInstantiator.instantiate();
} else {
client = new UrlConnectionClient();
} return new Provider() {
public Client get() {
return (Client)client;
}
};
}
所以推荐大家使用OKHttpClient
//3 初始化httpExecutor,其实Retrofit在执行请求时,用的还是Executor,所以这里需要初始化。这个参数Android和Base平台是一样的,代码如下:
Executor defaultHttpExecutor() {
return Executors.newCachedThreadPool(new ThreadFactory() {
public Thread newThread(final Runnable r) {
return new Thread(new Runnable() {
public void run() {
Process.setThreadPriority(10);
r.run();
}
}, "Retrofit-Idle");
}
});
}
其他的几个参数就不一一分析了,大家自己去看吧~
2. 创建真正给我们使用的Interface对象,比如:
final ApiManagerService apiManager = restAdapter.create(ApiManagerService.class);
2 WeatherData data = apiManager.getWeatherBody("Budapest,hu", "metric");
有了ApiManagerService对象,我们就能调用它的方法了。现在我们顺着RestAdapter.create方法去看看,这个对象是怎么创建的,以及我们调用方法时是如何执行的~
public <T> T create(Class<T> service) {
Utils.validateServiceClass(service);
return Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service},
new RestAdapter.RestHandler(this.getMethodInfoCache(service)));
}
看代码,使用动态代理的方式返回了一个Proxy对象,不知道动态代理的可自行百度~动态代理最终调用方法是在InvocationHandler中,我们来看RestAdapter.RestHandler~
到目前为止,我们知道了Retrofit是通过动态代理的方式来调用我们接口中的方法~
3. RestAdapter.RestHandler继承自InvocationHandler,重点看它的invoke()。invoke()将会是真正调用方法的地方,Retrofit在这个方法中,做了大量的判断和解析,当然,这些判断和解析是基于RestMethodInfo,它为每一个method创建了一个RestMethodInfo,我们先看看RestMethodInfo对象是怎么创建的,里面有哪些信息。
RestMethodInfo(Method method) {
this.requestType = RestMethodInfo.RequestType.SIMPLE;
this.method = method;
this.responseType = this.parseResponseType();
this.isSynchronous = this.responseType == RestMethodInfo.ResponseType.OBJECT;
this.isObservable = this.responseType == RestMethodInfo.ResponseType.OBSERVABLE;
}
以上是RestMethodInfo的构造器
requestType:代表的是请求类型,主要是用于解析和判断Annotation和请求类型,比如POST、GET亦或是FormUrlEncoded
method:就是实际要调用的方法
responseType:很重要,这里涉及到Retrofit定义方法的方式,代码如下:
private RestMethodInfo.ResponseType parseResponseType() {
Type returnType = this.method.getGenericReturnType();
Type lastArgType = null;
Class lastArgClass = null;
Type[] parameterTypes = this.method.getGenericParameterTypes();
if(parameterTypes.length > 0) {
Type hasReturnType = parameterTypes[parameterTypes.length - 1];
lastArgType = hasReturnType;
if(hasReturnType instanceof ParameterizedType) {
hasReturnType = ((ParameterizedType)hasReturnType).getRawType();
} if(hasReturnType instanceof Class) {
lastArgClass = (Class)hasReturnType;
}
} boolean hasReturnType1 = returnType != Void.TYPE;
boolean hasCallback = lastArgClass != null && Callback.class.isAssignableFrom(lastArgClass);
if(hasReturnType1 && hasCallback) {
throw this.methodError("Must have return type or Callback as last argument, not both.", new Object[0]);
} else if(!hasReturnType1 && !hasCallback) {
throw this.methodError("Must have either a return type or Callback as last argument.", new Object[0]);
} else if(hasReturnType1) {
if(Platform.HAS_RX_JAVA) {
Class rawReturnType = Types.getRawType(returnType);
if(RestMethodInfo.RxSupport.isObservable(rawReturnType)) {
returnType = RestMethodInfo.RxSupport.getObservableType(returnType, rawReturnType);
this.responseObjectType = getParameterUpperBound((ParameterizedType)returnType);
return RestMethodInfo.ResponseType.OBSERVABLE;
}
} this.responseObjectType = returnType;
return RestMethodInfo.ResponseType.OBJECT;
} else {
lastArgType = Types.getSupertype(lastArgType, Types.getRawType(lastArgType), Callback.class);
if(lastArgType instanceof ParameterizedType) {
this.responseObjectType = getParameterUpperBound((ParameterizedType)lastArgType);
return RestMethodInfo.ResponseType.VOID;
} else {
throw this.methodError("Last parameter must be of type Callback<X> or Callback<? super X>.", new Object[0]);
}
}
}
分解如下:
:1. 如果定义的方法有返回值,并且最后一个参数是Callback,那么该方法错误
:2. 如果定义的方法没有返回值,并且最后一个参数不是Callback,那么该犯方法错误
:3. 如果只有返回值,会判断返回值是否是Observable<WeatherData>,如果是,那么responseType是OBSERVABLE,否则是OBJECT
:4. 其他的都是没有返回值,但是有Callback的,responseType是VOID
isSynchronous:(this.responseType == RestMethodInfo.ResponseType.OBJECT),表示该方法是否是同步的,如果是同步的,则在当前线程调用,否则,异步调用。根据responseType的判断,我们发现:只有有返回值且返回值不是Observable类型的方法,是同步调用的。其他的都是异步调用。还记得前面提到过apiManager.getWeatherBody("Budapest,hu", "metric")是同步调用的吗?原因就在这里。
isObservable:只有返回值是Observable的方法才是,这个参数有特殊的用途,所以是一个独立的参数。
4. 真正分析invoke()方法
public Object invoke(Object proxy, Method method, final Object[] args) throws Throwable {
if(method.getDeclaringClass() == Object.class) {
return method.invoke(this, args); //忽略
} else {
final RestMethodInfo methodInfo = RestAdapter.getMethodInfo(this.methodDetailsCache, method);
if(methodInfo.isSynchronous) {
try {
return this.invokeRequest(RestAdapter.this.requestInterceptor, methodInfo, args);
} catch (RetrofitError var7) {
}
} else if(RestAdapter.this.httpExecutor != null && RestAdapter.this.callbackExecutor != null) {
if(methodInfo.isObservable) {
if(RestAdapter.this.rxSupport == null) {
if(!Platform.HAS_RX_JAVA) {
throw new IllegalStateException("Observable method found but no RxJava on classpath.");
} RestAdapter.this.rxSupport = new RxSupport(RestAdapter.this.httpExecutor, RestAdapter.this.errorHandler, RestAdapter.this.requestInterceptor);
} return RestAdapter.this.rxSupport.createRequestObservable(new RxSupport.Invoker() {
public ResponseWrapper invoke(RequestInterceptor requestInterceptor) {
return (ResponseWrapper)RestHandler.this.invokeRequest(requestInterceptor, methodInfo, args);
}
});
} else {
final RequestInterceptorTape interceptorTape = new RequestInterceptorTape();
RestAdapter.this.requestInterceptor.intercept(interceptorTape);
final Callback callback = (Callback)args[args.length - 1];
RestAdapter.this.httpExecutor.execute(new CallbackRunnable(callback, RestAdapter.this.callbackExecutor, RestAdapter.this.errorHandler) {
public ResponseWrapper obtainResponse() {
return (ResponseWrapper)RestHandler.this.invokeRequest(interceptorTape, methodInfo, args);
}
});
return null;
}
} else {
throw new IllegalStateException("Asynchronous invocation requires calling setExecutors.");
}
}
}
根据得到的RestMethodInfo:
1. 如果methodInfo.isSynchronous为true,那么invokeRequest,这个方法中调用this.clientProvider.get().execute(request)在当前线程
2. 如果需要异步调用,那么判断是不是Observable,
如果是:
判断平台是否支持RxJava环境,如果不支持,直接异常,如果支持,利用RxJava,由httpExecutor来执行。
如果不是:
那就是说,该方法有Callback,同样由httpExecutor执行,当然了,结果在Callback中返回,Callback方法在callbackExecutor中执行。如果是Android,那么Callback就是在UI线程。如果是Base,那个线程调用,在哪个线程中执行。
简单研究下Retrofit的更多相关文章
- 简单研究Loader笔记
2015-11-11 18:25:34 1. Loader是什么? /** * Static library support version of the framework's {@link and ...
- 学习和研究下unity3d的四元数 Quaternion
学习和研究下unity3d的四元数 Quaternion 今天准备学习和研究下unity3d的四元数 Quaternion 四元数在电脑图形学中用于表示物体的旋转,在unity中由x,y,z,w 表示 ...
- 简单了解下Dubbo
1. Dubbo是什么? Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案.简单的说,dubbo就是个服务框架,如果没有分布式的需求,其实是不需 ...
- SQLSERVER2012 列存储索引的简单研究和测试
SQLSERVER2012 列存储索引的简单研究和测试 SQLSERVER2012 列存储索引的简单研究和测试 看这篇文章之前可以先看一下下面这两篇文章: 列存储索引 http://www.cnblo ...
- k.tt 研究下生成的逻辑代码:从壹开始前后端分离 [.netCore 填坑 ] 三十二║ 四种方法快速实现项目的半自动化搭建
更新 1.更新小伙伴 @大龄Giser 提出好点子:试试VS的插件扩展:VSIX.ItemProject等,将T4模板给制作插件,这里先记下,有懂的小伙伴可以自己先试试,我会在以后更新. 2.感谢小伙 ...
- SQLSERVER中的LOB页面简单研究
SQLSERVER中的LOB页面简单研究 这篇文章和我另一篇文章是相辅相成的,在看<SQLSERVER2012 列存储索引的简单研究和测试>这篇文章之前希望大家先看一下这篇文章o(∩_∩) ...
- 简单聊下Unicode和UTF-8
今晚听同事分享提到这个,简单总结下. ## Unicode字符集 Unicode的出现是因为ASCII等其他编码码不够用了,比如ASCII是英语为母语的人发明的,只要一个字节8位就能够表示26个英文字 ...
- 简单聊下IO复用
没图,不分析API Java中IO API的发展:Socket -> SocketChannel -> AsynchronousSocketChannelServerSocket -> ...
- JQuery -> 超级简单的下拉菜单
使用jquery实现一个超级简单的下拉菜单. 效果图 最初的效果 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvRmVlTGFuZw==/font/5a6L ...
随机推荐
- 4.添加对efcore的支持 ,并使用mysql数据库。
1.添加并加入 在project.json中添加 "tools": { "Microsoft.EntityFrameworkCore.Tools": " ...
- Cheatsheet: 2016 07.01 ~ 07.31
Mobile What to Expect From Swift 3 Web A practical security guide for web developers Gulp Succinctly ...
- 微信小程序-表单
wxml <view> 按钮: <button size="{{buttom.size}}" type="{{buttom.type}}" p ...
- 浅谈C#Socket
好不容易把socket通信搞懂一点,比较喜欢做笔记,嘿嘿~ 希望共同学习,共同进步! socket通信是C#中非常基础的一个知识点,我这里用到的是基于Tcp协议的socket通信.Tcp会有三次握手连 ...
- Android开源测试框架学习
近期因工作需要,分析了一些Android的测试框架,在这也分享下整理完的资料. Android测试大致分三大块: 代码层测试 用户操作模拟,功能测试 安装部署及稳定性测试 代码层测试 对于一般java ...
- 使用SQL语句查询每张表的column name
exec sp_columns tableName 上面这行代码可以查出该表所有的column,改为sp_pkeys,可以查出该表的主键.但是我如果想只查出column name,select COL ...
- Apache 403 error, (13)Permission denied: access to / denied问题
Apache 配置Alias 后,无法访问 CentOS系统 检查了一圈httpd.conf和目录权限,均没有发现问题. 最后,看了这篇文章,发现是因为系统启动了SELINUX导致的. http:// ...
- Python学习---除法
python有两种除法,普通除法 a/b ,不论a,b精度 得到的都是浮点数. 4/2 = 2.0 3/5 = 0.6 floor除法,a//b , 得到一个舍弃小数位的整数结果,所以结果永远是 ...
- GNS3 IOU 配置
GNS3使用视频: http://edu.51cto.com/lesson/id-25295.html GNS3 IOU 与VM http://www.mamicode.com/info-detail ...
- UnsupportedClassVersionError 错误解决办法
偶然遇到关于版本问题的错误,为了以后查找方便记录下来.有更好的办法欢迎大家更正. 错误内容: Exception in thread "main" java.lang.Unsupp ...