在WebService中使用Retrofit+RxJava

最近Retrofit+RxJava还是非常火的,搭配一下MVP那开发简直就一个字爽。
但是现在很多比较老一点的公司还在使用WebService+xml,真的很让人感到忧伤。不过真的想使用Retrofit替代ksoap也不是不行的,至于效率到底高不高,还是要亲测才知道。
首先说一下WebService其实也是基于http的,也是同样可以用http去请求的。由于每个公司的协议都不同,所以在请求的时候,肯定是难以写出一个统一适用的请求框架的,最开始我是根据我公司WebService请求格式来定义各种请求用的Xml-bean。
具体可参照http://blog.csdn.net/huanghunhou705/article/details/51133893
这篇博客描述了如何构建请求用的xml。
首先贴一下我公司的WebService请求格式:

POST /WebService/UserService.asmx HTTP/1.1
Host: android.goodjob.cn
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://android.goodjob.cn/UserLogin"

<?xml version="1.0" encoding="utf-8"?>
<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/">
  <soap:Header>
    <AuthenticatedHeader xmlns="http://android.goodjob.cn/">
      <AccountID>string</AccountID>
      <Pin>string</Pin>
      <EncryptedMacAddress>string</EncryptedMacAddress>
    </AuthenticatedHeader>
  </soap:Header>
  <soap:Body>
    <UserLogin xmlns="http://android.goodjob.cn/">
      <userName>string</userName>
      <password>string</password>
    </UserLogin>
  </soap:Body>
</soap:Envelope>

响应格式:

HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length

<?xml version="1.0" encoding="utf-8"?>
<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/">
  <soap:Body>
    <UserLoginResponse xmlns="http://android.goodjob.cn/">
      <UserLoginResult>boolean</UserLoginResult>
    </UserLoginResponse>
  </soap:Body>
</soap:Envelope>

看上去好复杂。接下来我会根据我公司这种格式,来写一下如何使用Retrofit请求服务器数据.
首先我们先实例化一个Retrofit对象

HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();

        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

        Strategy strategy = new AnnotationStrategy();

        Serializer serializer = new Persister(strategy);

        Retrofit retrofit = new Retrofit.Builder()
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(SimpleXmlConverterFactory.create(serializer))
                .client(new OkHttpClient.Builder()
                        .addInterceptor(interceptor)
                        .build())
                .baseUrl("http://android.goodjob.cn/WebService/")
                .build();

在Retrofit中.client()方法可以传一个OkHttpClient对象,可以自己定义一个,配置缓存等等东西。这里为了讲述原理,只简单新建了一个。
接下来要写API了,用过Retrofit的同学都知道,我们都是通过这个api来定义请求参数,和返回参数的格式的。那应用在WebService上又应该怎么去写呢。

public interface UsStatesApi {

    @Headers({
            "Content-Type: application/soap+xml; charset=utf-8 ",
            "Accept-Charset: utf-8"
    })
    @POST("UserService.asmx")
    Observable<LoginResponseEnvelope> UserLogin(@Body LoginRequestEnvelope body);


}

看上去似乎还可以,接下来就是定义xml-bean了,这一部分内容我只简单描述一下,根据xml的格式建立对应的bean,目的是成功生成所需的xml和正确解析收到xml,为了保证正确性,之前在OkHttp中添加了log拦截器,可以看到发送的xml是否符合要求,我们来看看请求的格式,包含了一个soapHeader和一个soapBody。
所以我新建了一个LoginResponseEnvelope,来包含这两个对象,

@Root(name = "soap12:Envelope")
@NamespaceList({
        @Namespace(prefix = "xsi", reference = "http://www.w3.org/2001/XMLSchema-instance"),
        @Namespace(prefix = "xsd", reference = "http://www.w3.org/2001/XMLSchema"),
        @Namespace(prefix = "soap12", reference = "http://www.w3.org/2003/05/soap-envelope")
})
@Default
public class LoginRequestEnvelope {

    @Element(name = "soap12:Header", required = false)
    LoginRequestHeader body;


    @Element(name = "soap12:Body", required = false)
    LoginResquestBody header;


    public LoginRequestHeader getBody() {
        return body;
    }

    public void setBody(LoginRequestHeader body) {
        this.body = body;
    }

    public LoginResquestBody getHeader() {
        return header;
    }

    public void setHeader(LoginResquestBody header) {
        this.header = header;
    }

    @Override
    public String toString() {
        return "LoginRequestEnvelope{" +
                "body=" + body +
                ", header=" + header +
                '}';
    }
}

大概是这样,然后又接着定义Body-Bean和Header-Bean
还有根据响应的xml格式定义响应的xml-bean
最后弄下来大概也就10个左右文件
这里写图片描述
以上当然是反话,看到这一大堆的文件,第一反应就是一个请求竟然要做这么多复杂的操作,这完全违背了我们当初想要使用Retrofit的初衷嘛,Retrofit本来是为了简化网络操作的,结果弄出这么多类,完全已经把请求复杂化了好吗?第二反应大概就是,如果这些都能被复用就好了,通过泛型之类的?
反正博主尝试了非常多的方法,发现几乎都没有办法去复用这些,除了header以外,body总是要不停变化的,这意味着我们每次请求都要定义一堆body-bean。这太麻烦了。基本我在这个项目中实现了复用,换一种请求格式,又只能重头再来,两个字,心酸。
那到底有没有办法呢,其实还是有的,首先我们观察一下请求时的格式,其实仅仅只有参数不一样嘛,如果xml可以不使用框架去生成,而是我们自己去生成就好了,Retrofit为我们提供了自定义ConvertFactory的机会。我们可以自定义一个ConvertFactory。之前本来是想请求用String返回用框架解析对象的。但后来发现也存在很多问题,同样也是需要编写许多对应的bean文件,最后改成请求用String,响应也用String来接收,并自己实现xml的解析,当然也是用simple-xml,只是提前对响应结果做一些处理而已。
自定义ConvertFactory代码如下:

public class ToStringConverterFactory extends Converter.Factory {
  private static final MediaType MEDIA_TYPE = MediaType.parse("text/plain");

  @Override
  public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    if (String.class.equals(type)) {
      return new Converter<String, RequestBody>() {
        @Override public RequestBody convert(String value) throws IOException {
          return RequestBody.create(MEDIA_TYPE, value);
        }
      };
    }
    return null;
  }


  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
    if (String.class.equals(type)) {
      return new Converter<ResponseBody, String>() {
        @Override public String convert(ResponseBody value) throws IOException {
          return value.string();
        }
      };
    }
    return null;
  }
}

接下来我们可以自己定义自己的工具类用于拼接请求参数,这个就不多说了,根据各自的情况拼接就好。
api文件只需修改一下参数类型;

@Headers({
            "Content-Type: application/soap+xml; charset=utf-8 ",
            "Accept-Charset: utf-8"
    })
    @POST("UserService.asmx")
    Observable<String> UserLogin(@Body String body);

我实现了自己的拼接工具类之后,最后调用的方式为:

public static BaseEnvelope getInterview() {
        BaseEnvelope envelope = new BaseEnvelope("GetInterviewingCollection");
        String[] propertyName = {"myUserId", "pageIndex"};
        String[] propertyValue = {"****", "1"};
        envelope.setParamName(propertyName);
        envelope.setParamValue(propertyValue);
        return envelope;
    }

基本上配置一下参数名称和参数值就行了,当然你也可以有更好的方式去实现这个参数的拼接。最后结合Rxjava使用,是这样的。

IPositionApi api = retrofit.create(IPositionApi.class);

        BaseEnvelope envelope = PositionService.getInterview();
        api.getPositionList(envelope.getEnvelope())
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new ServerSubscriber<String>(iMain) {
                    @Override
                    public void onCompleted() {
                        iMain.showProgress(false);
                    }

                    @Override
                    public void onNext(String s) {
                        String result = XmlParseUtil.roundStart(s);
                        String ss = (String) XmlParseUtil.getXMLObject(result, String.class);
                        iMain.showData(ss);
                    }
                });

我自己写了一个XmlParseUtil的工具类,用于解析服务器返回的数据,当然,这个工具类并不是通用的,需要根据响应的格式做对应修改,但已经比之前简单太多了,复用性也高了很多,基本上只需要设置参数名参数值,返回时解析只需要传递一个类型的值就行了。
到这的话基本实现了WebService使用Retrofit+RxJava的内容了。
而且实现了复用。
接下来我说一下我在使用过程中的一些总结吧。由于OkHttp是属于比较成熟的网络框架,对网络请求的连接时间,响应时间,解析时间都能够观测,只需要简单谢一个拦截器即可,配置缓存也非常方便。但也存在一些问题,比如访问服务器报错的时候,这里是指发生了异常,在http中我不知道是否存在,但在WebService中如果服务器发生异常,在客户端这边是可以接收到错误信息,知道服务器那段代码出错,这样可以方便服务器那边修改。但使用了Retrofit之后,错误信息直接被拦截,最后只会抛出服务器异常,并不会有详细的服务器错误信息。不过如果在这方面没有什么需求,还是可以考虑使用。

本文在最开始也提到Retrofit其实也只是一个网络框架,如果你自己的项目中已经有一套自己熟悉的,并且效率较高的网络框架,其实我们还是可以使用自己的网络框架结合RxJava来使用的。
下一篇博客再更这方面内容吧。

最后对本文内容有任何疑问欢迎加群讨论:283272067

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值