在之前的博客里,采用了RxJava 1.1.10 和 Retrofit 2.1.0来搭建网络框架。在这片博客里,我们将采用RxJava2.x的版本来接入Retrofit进行搭建。由于RxJava2.x和RxJava1.x的版本差异性,所以这里我们把重点放在RxJava2的使用上。
项目地址:NetFrameWork。该项目中包含两个module,rxjava2和xmlformat,xmlformat是在rxjava2上做的改版,将JSON的传输格式改成XML传输格式的网络框架。而这套网络框架又是基于第一版RxRetrofitFramework的思想: Retrofit+RxJava的使用 。
版本更新内容:
- 对订阅者的改动:
RxJava 1 的订阅者是Subscriber,提供了onStart、onNext、onError、onCompleted四个方法,当需要取消订阅功能时,由Subscriber进行unsubscribe操作。
mSubscriber = new Subscriber<BaseServiceResult<T>>() {
@Override
public void onStart() {
super.onStart();
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(BaseServiceResult<T> t) {
// mCallback.doSomething
}
};
RxJava 2 的订阅者是Observer,提供了onSubscribe、onNext、onError、onComplete四个方法,当需要取消订阅功能时,由Observer的onSubscribe传递过来的对象Disposable 进行dispose操作。
mSubscriber = new Observer<NetServiceResult<T>>() {
@Override
public void onSubscribe(Disposable d) {
mDisposable = d;
}
@Override
public void onNext(NetServiceResult<T> t) {
// mCallback.doSomething
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
};
- 对重试策略的修订:
RxJava 1 和RxJava 2 的重试策略的操作符都是retryWhen,但由于改版,因此这里对触发onError之后的操作进行了变更,通过flatMap分发Exception,并判断该错误类型是否需要发送给订阅者错误信息或者进行返回数据源。
retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(@NonNull Observable<Throwable> throwableObservable) throws Exception {
return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(@NonNull Throwable throwable) throws Exception {
if (mNeedRetry && throwable instanceof IOException) {
mNeedRetry = false;
return Observable.timer(retryDelayMillis, TimeUnit.MILLISECONDS);
}
// For anything else, don't retry
return Observable.error(throwable);
}
});
}
})
在整个项目中,RxJava1 和RxJava2的应用最大的不同,就在于类NetProcessor
的升级。这个类也是对外接口暴露的核心内容,在设计思想上是一个由外部可以直接采用链式模式来对网络请求过程进行多元化处理,如取消请求、缓存回调、错误重试,甚至于还可以基于此进行项目自定制,如参数加密、排序处理,网络验证等等的一个工具。
XML格式传输
通常来讲,数据的网络传输格式一般都是JSON、XML,但JSON在应用中更为多见,JSON解析封装的工具更是非常之多。但不可否认的也有XML格式的需求,这里我们就讲如何封装XML请求和解析XML响应。
先来借鉴JSON项目的模式,我们最常见的是将具体的数据封装在一个统一的协议中进行响应。比如这样:
{
"code" : 0,
"msg" : "success",
"data" : {
...
}
}
因此我们才能在我们的设计框架中通过外层Bean的封装:BaseServiceResult<T>
,传入内层数据的class和泛型,利用JSON解析工具获取解析类型并回调回传。
然而,对于同样有着协议外壳XML的传输方式,在解析的时候,我们一般都是亲自采用SAX、DOM等等解析的方式,非常不方便代码的节省和统一的管理。因此,这里我们想到了SimpleXMLConvertFactory
这个工具。
和JSONConvertFactory
的使用方式和目的一致,都是用来解析数据的,但不同的地方就是,SimpleXMLConvertFactory
这个工具对Bean采用了注解的方式,而Retrofit的网络请求接口NetServer
在解析时,并不允许我们直接使用泛型T来获取响应内容,要么采用类似于NetServiceResult<T>
这种的解析方式。先解析外层协议,再解析内层数据。
但内层数据的获取,还得我们编写响应的代码去解析。但我们这里发现,SimpleXMLConvertFactory
工具中提供了类SimpleXmlResponseBodyConverter
的convert(ResponseBody value)
方法,可以直接解析响应内容。
@Override public T convert(ResponseBody value) throws IOException {
try {
T read = serializer.read(cls, value.charStream(), strict);
if (read == null) {
throw new IllegalStateException("Could not deserialize body as " + cls);
}
return read;
} catch (RuntimeException | IOException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
value.close();
}
}
因此,我们需要做的事情是:
1. 新建一个XmlResponseConverter
工具,用来解析ResponseBody
。
2. 将网络请求的返回类型由通用的外层协议直接改为ResponseBody
。
3. 新建响应Bean的model,继承协议类型,将data数据指向Bean并加上响应的注解,添加方法请求网络。,或者不存在统一的外层协议时,直接继承Bean,添加网络请求,见IPModel
。
4. 在执行请求后,通过工具进行解析。
于是,我们可以通过上述方案得到对应的解析对象了。
以上我们完成了响应,接下来该完成请求对象的封装,比如说,我们向服务器提交一个POST的BODY请求,如何转换为XML,其实很简单,利用SimpleXMLConvertFactory
就可以达到。也就是说,有XmlResponseConverter
工具来解析响应,就有XmlRequestConverter
来封装请求。
但是这个工具要求提供参数Request,但我们并没有方式直接构建一个Request,因此,这里我们剩下最省事的办法就是利用Retrofit提供的接口添加一个ConvertFactory
,利用这个转换器来达到目的。因此我们就要构建自己的转换器,一是要能够封装请求参数,二是要屏蔽响应解析。
因为类的包保护机制,所以我们可以复制SimpleXMLConvertFactory
代码并做相应的改动。
public class XMLConvertFactory extends Converter.Factory {
public static XMLConvertFactory create() {
return create(new Persister());
}
/** Create an instance using {@code serializer} for conversion. */
public static XMLConvertFactory create(Serializer serializer) {
return new XMLConvertFactory(serializer, true);
}
/** Create an instance using a default {@link Persister} instance for non-strict conversion. */
public static XMLConvertFactory createNonStrict() {
return createNonStrict(new Persister());
}
/** Create an instance using {@code serializer} for non-strict conversion. */
@SuppressWarnings("ConstantConditions") // Guarding public API nullability.
public static XMLConvertFactory createNonStrict(Serializer serializer) {
if (serializer == null) throw new NullPointerException("serializer == null");
return new XMLConvertFactory(serializer, false);
}
private final Serializer serializer;
private final boolean strict;
private XMLConvertFactory(Serializer serializer, boolean strict) {
this.serializer = serializer;
this.strict = strict;
}
public boolean isStrict() {
return strict;
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
// 屏蔽响应解析
return null;
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
if (!(type instanceof Class)) {
return null;
}
// 创建请求封装
return new XmlRequestBodyConverter<>(serializer);
}
}
final class XmlRequestBodyConverter<T> implements Converter<T, RequestBody> {
private static final MediaType MEDIA_TYPE = MediaType.parse("application/xml; charset=UTF-8");
private static final String CHARSET = "UTF-8";
private final Serializer serializer;
XmlRequestBodyConverter(Serializer serializer) {
this.serializer = serializer;
}
@Override public RequestBody convert(T value) throws IOException {
Buffer buffer = new Buffer();
try {
OutputStreamWriter osw = new OutputStreamWriter(buffer.outputStream(), CHARSET);
serializer.write(value, osw);
osw.flush();
} catch (Exception e) {
throw new RuntimeException(e);
}
return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
}
}
这里唯一的不同就在于XMLConvertFactory
对于responseBodyConverter
的处理,这里返回null表示不进行任何处理。至此,我们就完成了XML在Retrofit的使用。
如果有任何疑问,欢迎留言讨论。