在Retrofit源码分析几篇博文中详细分析了Retrofit的内部原理,本篇再此基础上对Retrofit做了简单的封装。这次封装是基于所在项目中的情况进行的,可能不具有通用性。但是也算是一个基本思路。 如下图: 在看项目代码的时候发现如上图的接口方法很多,都有一个共性:就是方法参数一样,都是传一个map。不同的地方只是方法名字不同而已。
根据Retrofit源码分析系列博文我们知道每一个接口都会生成一个ServiceMethod对象并缓存起来。在项目中发现了数百个类似的接口,也就是说需要创建数百个ServiceMethod对象。现在有点不合适,所以我就对其进行了改造。
第一步:创建一个通用的post和get接口类,直接上代码:
interface BaseApi {
//带参数的通用get请求
@GET()
Call executeGet( @Url String url, @QueryMap Map maps);
//不带参数的通用get请求
@GET()
Call executeGet(@Url String url);
//不带参数的通用post请求
@POST()
Call executePost( @Url String url);
//带参数的通用post请求
@POST()
@FormUrlEncoded
Call executePost( @Url String url, @FieldMap Map maps);
}
上面的get方法和post方法其返回值都是Call
,那么我怎么怎么样自动返回所需的JavaBean呢,别急;且看第二步:
第二步:实现通用的get和post请求
先以同步get请求为例,为了转换特定类型的JavaBean,我们必须知道其Type类型,所以具体的get方法如下所示:
BaseApi baseApi = retrofit.create(BaseApi.class);
//不带参数的同步get请求
public T doGetSync(Class type, final String url) {
//执行接口的get请求
Call call = baseApi.executeGet(url);
//执行网络请求
Response response = call.execute();
//获取baseApi的class对象
Class clas = baseApi.getClass();
//获取executeGet的Method对象
Method method = clas.getMethod("executeGet", new Class[]{String.class});
//核心方法,查找对应的Converter对象
Converter converter = retrofit.responseBodyConverter(type, method.getAnnotations());
//进行对应的JavaBean转换
if (converter != null) {
return (T) converter.convert(response.body());
}
return null;
}
上面方法关键有两处: 1、执行 baseApi.executeGet(url)方法获取一个Call
对象,然后根据这个call对象执行execute同步请求方法,返回Response
对象。 2、根据method的注解以及方法参数的type类型,调用retrofit.responseBodyConverter方法来获取对应的Converter对象,执行转换即可。 关键Converter的具体工作流程可查看Retrofit之Converter简单解析
使用起来也很简单:
YourBean bean = doGetSync(YourBean.class,"you api url")
异步请求有点特殊,因为需要回调结果给客户端,所以另外设置了一个接口:
public interface IResponse {
void success(D data);
void failure(Throwable t);
/**
* 返回JavaBean的class 对象 return YouBean.class
* 以此来确定要转换的类型
* @return
*/
Type getDataType();
}
那么一步请求就如下所示:
public void doGetAsync(final String url, final IResponse res) {
Call call = baseApi.executeGet(url);
try {
//异步请求
call.enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
if (res == null) {
return;
}
try {
Class clas = baseApi.getClass();
Method method = clas.getMethod("executeGet", new Class[]{String.class});
//根据resonse的getDataType来决定返回的类型
Converter converter = retrofit.responseBodyConverter(res.getDataType(), method.getAnnotations());
if (converter != null) {
T data = (T) converter.convert(response.body());
res.success(data);
}
} catch (Exception e) {
res.failure(e);
}
}
@Override
public void onFailure(Call call, Throwable t) {
if (res != null) res.failure(t);
}
});
} catch (Exception e) {
if (res != null) res.failure(e);
}
}
代码就是上面的代码,别的不多说了,直接贴如何使用的代码:
doGetAsync("http://api.apiopen.top/singlePoetry", new ZmHttp.IResponse() {
@Override
public void success(Bean data) {
//请求成功
}
@Override
public void failure(Throwable t) {
//请求失败
}
@Override
public Class getDataType() {
return Bean.class;
}
});
到此为止,简单的封装挑选完毕。本次封装只是解决了如上文图中代码所示的api方法参数相同而方法名不同,导致方法越来越多的问题。如有不当之处欢迎批评指正。源码稍后风尚