retrofit2集成(webService、http)
适用环境
1、项目中既有webService服务,也有http服务;
2、项目原有服务是webService或者http其中的一种,由于需求变更等原因要更换服务类型,同时要在不影响原来的服务请求的情况下进行网络框架重构;
3、写服务的换人了;<或者服务人员想装逼,彰显他会多种服务语言>
retrofit2简单介绍
retrofit是由Square公司出品的针对于Android和Java的类型安全的Http客户端。
简单的说就是一种网络请求框架,对okHttp的封装,使用面向接口的方式进行网络请求,利用动态生成的代理类封装了网络接口请求的底层,其将请求返回javaBean,对网络认证 REST API进行了很好对支持此,使用Retrofit将会极大的提高我们应用的网络体验。
【其实自己是看了github上的网络框架使用统计,排名第一的就是retrofit,所以就去稍稍的研究了一下,发现还挺好用,毕竟这热度可不是水军给刷上去的。】
下面就以我自己的项目中的例子来给大家演示一下今天的核心内容:
实例讲解
一、情景重现
为了迎合后台服务人员(以前公司的后台只会写webService)的服务类型,我们移动端选择采用比较常用ksoap网络请求框架来请求webService服务,首先以图片的形式看一下项目中ksoap网络请求的封装类。
以上就是原来项目中用到的ksoap网络请求帮助类,看起来也还OK,但是在实际的项目中,每到一次网络请求就要new出来一个handler或者asyncTask去处理网络请求,并且代码写起来很长很冗余,导致一个页面有多个请求的时候,光关于网络请求的代码就一大堆。另外原来的项目也灭有框架可言,基本大部分的代码都在activity或者fragment中,导致界面工作效率过低,代码过于混乱。
【这里有个不大不小的坑,注意图片中红色部分,留着到后面再讲QAQ】
基于这样的情况,项目组经过商议后决定要重构项目,其中就包括网络请求框架。要求在不改变以前程序代码的基础下进行代码重构。所以经过慎重考虑后,决定将retrofit2集成进我们的项目中去。
二、retrofit集成
1、首先导入retrofit2的相关依赖:
//retrofit网络框架
compile 'com.squareup.retrofit2:retrofit:2.3.0' //retrofit2.3.0类库
compile'com.squareup.retrofit2:converter-scalars:2.3.0' //关于字符串的转换器
compile 'com.squareup.retrofit2:converter-gson:2.3.0' //关于gson的转换器
compile 'com.squareup.okhttp3:logging-interceptor:3.4.1' //okhttpClient的log拦截器
2、http请求:
依赖包导入成功后就可以放肆的利用retrofit2进行实验了,首先用一个简单的http请求测试一下是否能走通。
public interface PostApi {
/**
* 上传错误信息接口
* */
@POST("api/OPERATION_ERROR_STATISTICS")
Call<ResponseBody> postError(@Body ErrorBean bean);
}
以上是一个简单的post请求接口。
【实体类就不贴了,自定义任何一个实体类都可以】
然后是请求的方法及回调:
public void errorPost(List<CrashBean> list){
//创建retrofit
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ActionNet.BASE_URL)
.client(getOkHttpClient())
.addConverterFactory(GsonConverterFactory.create())
.build();
PostApi api = retrofit.create(PostApi.class);
ErrorBean bean = new ErrorBean();
bean.setData(list);
Call<ResponseBody> call = api.postError(bean);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
if(response.body() != null){
String result = response.body().string();
Log.e(TAG+"Error", "onResponse: "+result);
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e(TAG+"Error", "onFailure: ");
}
});
}
retrofit2的具体实现我就不说了,相信大家都已经了解过retrofit2了。
程序走了一遍可以走通,证明了retrofit2是可以正常使用的。
3、retrofit2请求webService
好了,轮到本文重头戏上场了。
首先,尝试着使用retrofit2请求webService:
【当然对于这个刚开始我是一脸懵逼的,要不是网上有一些关于retrofit2请求webService的文章,我还真的不知道从何下手,毕竟webService的请求接口中注解有点儿复杂】
/**
* 获取补丁版本
* */
@Headers({“Content-Type: text/xml;charset=UTF-8”, “SOAPAction: http://tempuri.org/getNewAppPatch“}) //请求的Action,类似于方法名
@POST(“GetTJWebService.asmx”)
Call getNewAppPatch(@Body String envelope);
看到webService请求接口里的请求头了吧,超复杂的感觉。【注意我的请求实体类是String,使用String是为了所有的请求都能公用一个类,所以就把实体类转换成String并进行封装(当然simplexml的转换器可以自动将实体类进行解析,但是这样太麻烦,每一个请求都要写一大堆转换类)】
首先要知道webService服务是以xml的形式进行请求并返回数据,这里给大家贴一下webService的请求
可以看到,webService的请求和返回都是有固定格式的要求(这里用的是soap1.1版本),所以这里为了使所有的webService请求都能适配,我这里封装了一个自定义的请求及返回数据的处理类,即webService的请求和接收都是将参数或者返回数据处理成String字符串进行请求和接收。代码如下:
public class Node {
public static String toStart(String name){
return "<"+name+"@gt;";
}
public static String toEnd(String name){
return "</"+name+">";
}
public static String getRequest(String namespace, Map<String,String> map){
StringBuffer sbf = new StringBuffer();
String startStr = "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
+" <soap:Body>\n"
+" <"+namespace+" xmlns=\"http://tempuri.org/\">\n";
sbf.append(startStr);
for (Map.Entry<String,String> entry : map.entrySet()){
sbf.append(" <"+entry.getKey()+">"+entry.getValue()+"</"+entry.getKey()+">\n");
}
String endStr = " </"+namespace+">\n"
+" </soap:Body>\n"
+"</soap:Envelope>";
sbf.append(endStr);
return sbf.toString();
}
}
这是请求的封装,下面是接收的封装:
public Map<String,String> dealResponse(Response<ResponseBody> response){
Map<String,String> map = new HashMap<>();
String head = response.raw().request().header("SOAPAction");
String url = response.raw().request().url().toString();
String type = null,data = null,res = null;
try {
if(head != null){ //webService请求
String name = head.replace(AppNet.NAME_SPACE, "");
type = url+"/"+name;
data = response.body().string().replaceAll("<","<").replaceAll(">",">");
res = StringUtils.substringBetween(data,"<"+name+"Result>","</"+name+"Result>");
}else { //Http请求
type = url;
data = response.body().string();
res = data;
}
} catch (IOException e) {
e.printStackTrace();
}
map.put("data",res);
map.put("url",type);
return map;
}
这样请求和接收的转换都有了,现在就来测试一下请求webService。
请求接口上面已经贴过了,下一步就是创建retrofit,这里我进行了一下封装。
//创建retrofit
public void creatRetrofit(){
if(retrofit == null){
OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder();
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = okHttpClient.connectTimeout(2, TimeUnit.MINUTES)
.writeTimeout(2, TimeUnit.MINUTES)
.readTimeout(2, TimeUnit.MINUTES)
.addInterceptor(httpLoggingInterceptor)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(this.BASE_URL)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
}
if(mService == null){
mService = retrofit.create(UserService.class);
}
}
【注意:ScalarsConverterFactory一定要加上,不然无法将String字符串转换成xml的请求格式,这个坑我可是看了好久才搞出来,一定要注意细节!】
其中UserService是我的接口类。
然后就是发出请求:
public void getVerson(String xml, Callback<ResponseBody> callback){
Call<ResponseBody> call = mService.getNewAppPatch(xml);
call.enqueue(callback);
}
请求之后是回调:
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if(response.body() != null){
map = model.dealResponse(response);
data = map.get("data");
url = map.get("url");
if(model.isWebService(response)){
//进行xml解析
switch (url){
case "":
break;
}
}else {
//进行json解析
switch (url){
case "":
break;
}
}
}else {
Log.e("fail", "requestFailed");
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e("error", "requestError");
}
【这里的回调和上面的返回数据转换,我把webService和http都进行了区分,这样不管是webService还是http都可以公用一个回调,适用于一个页面既有webService也有http请求,并且都是多个请求。】
本以为这样就可以万事大吉,但是点一下运行,程序报错了,我们来看看报错内容吧:
一目了然,依赖包冲突,而且是OkHttp3的依赖包冲突。心想,我的项目里没有导入OkHttp啊,哪来的冲突?于是乎我就在项目中进行寻找,还真让我找到了:
原来如此,ksoap的jar包里包含了okhttp3,而且retrofit是强制封装了OkHttp3,所以才会有冲突,这样的话就只好去重了啊,我的方法是这样的,把ksoap包里的okhttp3和okio这两个包给删掉,然后我们在文章顶部中soapUtils类里,把红色部分改为HttpTransportSE就可以了。
然后再运行,项目完完整整的跑出来了。这就是将retrofit2集成到同时具有webService和http请求的项目中去的解决方案了。
总结
1、把webService的请求和接收封装起来,这样使请求更加方便。
2、在retrofit2请求webService的时候,如果要使用String转换的话一定要加上ScalarsConverterFactory的转换器;
3、ksoap的jar包里是有okhttp请求的,所以retrofit和ksoap会有冲突依赖,根据实际情况解除冲突;
4、本文没有具体的说明retrofit请求http和webService的教程,如果大家不是很了解,建议去看看其他博主的博客,如:
http://blog.youkuaiyun.com/lmj623565791/article/details/51304204 retrofit2完全解析
http://www.jianshu.com/p/b865c855a1e8 retrofit2 + OkHttp + WebService请求
5、建议你的后台人员只写一种服务,因为这样的多种请求真是要搞死人QAQ。
本博主第一次写这么长的文章,希望大家多多支持,有什么不对的还希望大家多多指正。
代码还没来得及上传到github,如有需要请留言。
谢谢侬!(鞠躬)