Android RxJava+Retrofit統一處理API返回,根據返回值決定是否Retry,綁定Activity生命周期
假設有個登錄API,登錄返回的值是
{"code":0,msg:"登錄成功","data":{"username":"xxx","nikename":"xxx"...}}
{"code":-100,msg:"用戶不存在","data":null}
{"code":-101,msg:"密碼錯誤","data":null}
...
用其他框架Http請求的時候,比如默認回調是HttpCallback,我們一般會對這個HttpCallback加一層封裝比如MyHttpCallback,在MyHttpCallback里面
1) JSON轉成Bean //也有很多框架自帶Gson
2) code<0的時候彈出一個Toast顯示msg
OkHttpUtils
.get()
.url("http://xxx")
.addParams("username", "xxx")
.addParams("password", "xxx")
.build()
.execute(new MyHttpCallback<User>() {
@Override
public void onError(Exception e) {
}
@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的功能呢?
1) 一般第一反應就是在Subscriber的onNext里面去判斷,這樣的寫法滿足不了這樣的需求:code<0的時候要Retry
2) Retrofit我們都采用Gson處理返回的數據,如果我返回的結果比較簡單,比如根據手機號返回一個驗證碼{"code":0,msg:"獲取驗證碼成功","data":"8451"},還是要建立一個Bean類,有點麻煩,我想不寫這個Bean類,在onNext傳入的參數可不可以直接是JSONObject或者String
3) 我們會經常在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);
不是鏈式了,不好看。
4) 我們的項目里應該有很多公共的部分
.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 DX168Subscriber<User>(getApplicationContext()) {
@Override
public void onSuccess(User user) {
//TODO
}
});
DX168API.get()
.getRegisterVerifyCode("18888888888")
.retryWhen(new RetryWhenNetworkException())
.compose(new DX168Transformer())
.lift(new BindActivityOperator(this))
.subscribe(new DX168Subscriber<String>(getApplicationContext()) {
@Override
public void onSuccess(String data) {
//TODO
// data就是verifyCode
}
});
}
}
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) {
//如果返回結果是JSONObject或者DX168Response則無需經過Gson
if (type.toString().equals(JSONObject.class.toString())) {
return (T) response;
} else if (type.toString().equals(DX168Response.class.toString())) {
Object data = response.opt("data");
DX168Response dx168Response = new DX168Response(code, msg, data);
return (T) dx168Response;
} else {
return gson.fromJson(value, type);
}
} else {
//返回的code不是RESULT_OK時Toast顯示msg
throw new DX168Exception(code, msg, value);
}
} catch (JSONException e) {
//服務端返回的不是JSON,服務端出問題
throw new DX168Exception(-1, "", value);
}
}
}
public abstract class DX168Subscriber<T> extends Subscriber<DX168Response> {
private Context context;
public DX168Subscriber(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 void onNext(DX168Response dx168Response) {
Object data = dx168Response.getData();
if (data == JSONObject.NULL) {
data = null;
}
onSuccess((T) data);
}
public abstract void onSuccess(T data);
@Override
public void onCompleted() {
}
public void onDX168Exception(DX168Exception e) {
Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show();
}
public void onNetworkException(Throwable e) {
Toast.makeText(context, "網絡較慢,請稍候...", Toast.LENGTH_SHORT).show();
}
public void onUnknownException(Throwable e) {
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;
public void addSubscriber(Subscriber subscriber, ActivityLifecycle unsubscribeOn) {
if (subscribers == null) {
subscribers = new ArrayList<>();
}
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 onStop() {
for (SubscriberWrapper wrapper : subscribers) {
if (wrapper.unsubscribeOn == ActivityLifecycle.OnStop) {
wrapper.subscriber.unsubscribe();
subscribers.remove(wrapper);
}
}
super.onStop();
}
@Override
protected void onDestroy() {
for (SubscriberWrapper wrapper : subscribers) {
if (wrapper.unsubscribeOn == ActivityLifecycle.OnDestroy) {
wrapper.subscriber.unsubscribe();
subscribers.remove(wrapper);
}
}
super.onDestroy();
}
}
就這樣吧,有興趣的看看代碼,不清楚的或者有不足的地方,歡迎交流。
來自:http://www.jianshu.com/p/930d7d728230