RxJava Retrofit 封装
关于绑定生命周期的,推荐我的新设计
RxLifecycle的替代方案
假设有个登录API,登录返回的值是
{"code":0,msg:"登录成功","data":{"username":"xxx","nikename":"xxx"...}}
{"code":-100,msg:"用户不存在","data":null}
{"code":-101,msg:"密码错误","data":null}
...
用其他框架Http请求的时候,比如默认回调是HttpCallback,我们一般会对这个HttpCallback加一层封装比如MyHttpCallback,在MyHttpCallback里面
-
JSON转成Bean
-
code<0的时候弹出一个Toast显示msg
OkHttpUtils
.get()
.url("http://xxx")
.addParams("username", "xxx")
.addParams("password", "xxx")
.build()
.execute(new MyHttpCallback<User>() {
@Override
public void onFailure(int code, String msg) {
}@Override public void onSuccess(int code, String msg, User user) { } });
返回分为3种情况:
第一种是连接服务端失败(比如常见的404、500、502等)
第二种是请求成功但服务端告知我们参数有误
第三种是完全正确的
那使用RxJava+Retrofit该如何写出效果类似MyHttpCallback的功能呢?
-
一般就是在Subscriber的onNext里面去判断,这样的写法满足不了这样的需求:code<0的时候要Retry
-
Retrofit我们都采用Gson处理返回的数据,如果我返回的结果比较简单,比如根据手机号返回一个验证码{"code":0,msg:"获取验证码成功","data":"8451"},还是要建立一个Bean类,有点麻烦,我想不写这个Bean类,在onNext传入的参数可不可以直接是JSONObject
-
我们会经常在onNext里面去处理UI,那我们应该知道需要在Activity的onDestroy()取消订阅。第一想法就是在Activity声明一个全局变量
private CompositeSubscription compositeSubscription;
每次订阅的时候都添加到compositeSubscription
Subscription subscription = xxx
.xxx()
.retryWhen(xxx)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
.subscribe(xxx);
compositeSubscription.add(subscription);
然后在onDestroy()里面
compositeSubscription.unsubscribe();
这样写法没什么问题,只是每次订阅都要重复这2句
Subscription subscription = xxx;
compositeSubscription.add(subscription);
不是链式了,不好看。
-
我们的项目里应该有很多公共的部分
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
比如我的项目里这部分都是一样的,每个请求都写这么长一串也不太好看。
直接上代码,看看最终的写法
public class MainActivity extends BaseActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onClick(View view) {
DX168API.get()
.login("xxx", "xxx")
.retryWhen(new RetryWhenNetworkException()) //可以设置重试次数,延迟重试
.compose(new DX168Transformer()) //一些通用的处理,如subscribeOn和observeOn
.lift(new BindActivityOperator(this)) //绑定Activity,可以指定Activity的生命周期,用来取消订阅
.subscribe(new BaseSubscriber<User>(getApplicationContext()) {
@Override
public void onNext(User user) {
//TODO
}
});
DX168API.get()
.getRegisterVerifyCode("18888888888")
.retryWhen(new RetryWhenNetworkException())
.compose(new DX168Transformer())
.lift(new BindActivityOperator(this))
.subscribe(new BaseSubscriber<JSONObject>(getApplicationContext()) {
@Override
public void onNext(JSONObject data) {
//TODO
}
});
}
}
public class DX168GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final Type type;
DX168GsonResponseBodyConverter(Gson gson, Type type) {
this.gson = gson;
this.type = type;
}
@Override
public T convert(ResponseBody responseBody) throws IOException {
String value = responseBody.string();
try {
JSONObject response = new JSONObject(value);
int code = response.optInt("code");
String msg = response.optString("msg");
if (code != DX168API.RESULT_OK) {
//返回的code不是RESULT_OK时Toast显示msg
throw new DX168Exception(code, msg, value);
}
if (type instanceof Class) {
if (type == String.class) {
return (T) value;
}
if (type == JSONObject.class) {
//如果返回结果是JSONObject则无需经过Gson
return (T) response;
}
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
if (parameterizedType.getRawType() == DX168Response.class) {
String data = response.optString("data");
Type dataType = parameterizedType.getActualTypeArguments()[0];
if (dataType == JSONObject.class) {
return (T) new DX168Response(code, msg, new JSONObject(data));
}
}
}
return gson.fromJson(value, type);
} catch (JSONException e) {
//服务端返回的不是JSON,服务端出问题
throw new DX168Exception(-1, "", value);
}
}
}
public abstract class BaseSubscriber<T> extends Subscriber<T> {
protected Context context;
public BaseSubscriber() {
}
public BaseSubscriber(Context applicationContext) {
this.context = applicationContext.getApplicationContext();
}
@Override
public void onError(Throwable throwable) {
Throwable e = throwable;
while (throwable.getCause() != null) {
e = throwable;
throwable = throwable.getCause();
}
if (e instanceof ConnectException || e instanceof SocketTimeoutException || e instanceof TimeoutException) {
onNetworkException(e);
} else if (e instanceof DX168Exception) {
onDX168Exception((DX168Exception) e);
} else {
onUnknownException(e);
}
}
@Override
public abstract void onNext(T t);
@Override
public void onCompleted() {
}
public void onDX168Exception(DX168Exception e) {
e.printStackTrace();
if (context != null) {
Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
public void onNetworkException(Throwable e) {
if (context != null) {
Toast.makeText(context, "网络较慢,请稍候...", Toast.LENGTH_SHORT).show();
}
}
public void onUnknownException(Throwable e) {
e.printStackTrace();
if (context != null && BuildConfig.BUILD_TYPE.equals(Constants.BUILD_TYPE_DEBUG)) {
Toast.makeText(context, e.toString(), Toast.LENGTH_SHORT).show();
}
}
}
public class RetryWhenNetworkException implements Func1<Observable<? extends Throwable>, Observable<?>> {
private int count = 5;
private long delay = 5000;
private long increaseDelay = 5000;
public RetryWhenNetworkException() {
}
public RetryWhenNetworkException(int count, long delay) {
this.count = count;
this.delay = delay;
}
public RetryWhenNetworkException(int count, long delay, long increaseDelay) {
this.count = count;
this.delay = delay;
this.increaseDelay = increaseDelay;
}
@Override
public Observable<?> call(Observable<? extends Throwable> observable) {
return observable
.zipWith(Observable.range(1, count + 1), new Func2<Throwable, Integer, Wrapper>() {
@Override
public Wrapper call(Throwable throwable, Integer integer) {
return new Wrapper(throwable, integer);
}
}).flatMap(new Func1<Wrapper, Observable<?>>() {
@Override
public Observable<?> call(Wrapper wrapper) {
if ((wrapper.throwable instanceof ConnectException
|| wrapper.throwable instanceof SocketTimeoutException
|| wrapper.throwable instanceof TimeoutException)
&& wrapper.index < count + 1) { //如果超出重试次数也抛出错误,否则默认是会进入onCompleted
return Observable.timer(delay + (wrapper.index - 1) * increaseDelay, TimeUnit.MILLISECONDS);
}
return Observable.error(wrapper.throwable);
}
});
}
private class Wrapper {
private int index;
private Throwable throwable;
public Wrapper(Throwable throwable, int index) {
this.index = index;
this.throwable = throwable;
}
}
}
public class BaseActivity extends Activity {
private List<SubscriberWrapper> subscribers = new LinkedList<>();
public void addSubscriber(Subscriber subscriber, ActivityLifecycle unsubscribeOn) {
subscribers.add(new SubscriberWrapper(subscriber, unsubscribeOn));
}
private class SubscriberWrapper {
Subscriber subscriber;
ActivityLifecycle unsubscribeOn;
public SubscriberWrapper(Subscriber subscriber, ActivityLifecycle unsubscribeOn) {
this.subscriber = subscriber;
this.unsubscribeOn = unsubscribeOn;
}
}
@Override
protected void onDestroy() {
Iterator<SubscriberWrapper> it = subscribers.iterator();
while (it.hasNext()) {
SubscriberWrapper subscriberWrapper = it.next();
if (subscriberWrapper.unsubscribeOn == ActivityLifecycle.OnDestroy) {
subscriberWrapper.subscriber.unsubscribe();
it.remove();
}
}
super.onDestroy();
}
}