简单研究下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 ...
随机推荐
- 如何使用JDBC实现数据访问对象层(DAO)
JAVA是面向对象的语言,开发者在操作数据的时候,通常更习惯面对一个特定类型的对象,如一个用户就是一个User类的对象.DAO层需要做的,就是为上层提供充分的对象支持,让上层再也看不到具体的数据,而是 ...
- 微信小程序-WebSocket
wx.connectSocket(OBJECT) 创建一个 WebSocket 连接:一个微信小程序同时只能有一个 WebSocket 连接,如果当前已存在一个 WebSocket 连接,会自动关闭该 ...
- gbd基本使用一
http://biancheng.dnbcw.info/linux/391846.html
- 标准W3C盒子模型和IE盒子模型CSS布局经典盒子模型(转)
盒子模型是css中一个重要的概念,理解了盒子模型才能更好的排版.其实盒子模型有两种,分别是 ie 盒子模型和标准 w3c 盒子模型.他们对盒子模型的解释各不相同,先来看看我们熟知的标准盒子模型: 从上 ...
- Linux-ubuntu指令使用积累(长期更新)
alias cat cd cp ls mkdir mv rm sudo tar chmod 1. sudo 系统管理指令.放在其它指令之前使用,允许普通用户在root权限下执行部分或者全部 ...
- 编译2.4.X apache 常见错误
安装高版本的 apr apr-util ./configure prefix=/usr/local/apr ./configure prefix=/usr/local/apr-util -- ...
- json数组转普通数组 普通数组转json数组
1.json_decode() json_decode (PHP 5 >= 5.2.0, PECL json >= 1.2.0) json_decode — 对 JSON 格式的字符串进行 ...
- java数据库连接池技术原理(浅析)
在执行数据库SQL语句时,我们先要进行数据连接:而每次创建新的数据库的连接要消耗大量的资源,这样,大家就想出了数据库连接池技术.它的原理是,在运行过程中,同时打开着一定数量的数据库连接,形成数据连接池 ...
- java模拟面试 试题
java 四类八种基本数据类型 第一类:整型 byte short int long 第二类:浮点型 float double 第三类:逻辑型 Boolean(取值为 true false) 第四类: ...
- NotORM(PHP的ORM框架)
类似Thinkphp映射到表的ORM机制. 网址:http://www.notorm.com/ <?php //require_once ‘init.php’;require_once ‘Not ...