Android RxJava+Retrofit统一处理API返回,根据返回值决定是否Retry,绑定Activity生命周期
oryx
8年前
<p>假设有个登录API,登录返回的值是<br> {"code":0,msg:"登录成功","data":{"username":"xxx","nikename":"xxx"...}}<br> {"code":-100,msg:"用户不存在","data":null}<br> {"code":-101,msg:"密码错误","data":null}<br> ...</p> <p>用其他框架Http请求的时候,比如默认回调是HttpCallback,我们一般会对这个HttpCallback加一层封装比如MyHttpCallback,在MyHttpCallback里面<br> 1) JSON转成Bean //也有很多框架自带Gson<br> 2) code<0的时候弹出一个Toast显示msg</p> <pre> <code class="language-java">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) { } });</code></pre> <p>返回分为3种情况:<br> 第一种是连接服务端失败(比如常见的404、500、502等)<br> 第二种是请求成功但服务端告知我们参数有误<br> 第三种是完全正确的</p> <p>那使用RxJava+Retrofit该如何写出效果类似MyHttpCallback的功能呢?<br> 1) 一般第一反应就是在Subscriber的onNext里面去判断,这样的写法满足不了这样的需求:code<0的时候要Retry<br> 2) Retrofit我们都采用Gson处理返回的数据,如果我返回的结果比较简单,比如根据手机号返回一个验证码{"code":0,msg:"获取验证码成功","data":"8451"},还是要建立一个Bean类,有点麻烦,我想不写这个Bean类,在onNext传入的参数可不可以直接是JSONObject或者String<br> 3) 我们会经常在onNext里面去处理UI,那我们应该知道需要在Activity的onDestroy()取消订阅。第一想法就是在Activity声明一个全局变量</p> <pre> <code class="language-java">private CompositeSubscription compositeSubscription;</code></pre> <p>每次订阅的时候都添加到compositeSubscription</p> <pre> <code class="language-java">Subscription subscription = xxx .xxx() .retryWhen(xxx) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); .subscribe(xxx); compositeSubscription.add(subscription);</code></pre> <p>然后在onDestroy()里面</p> <pre> <code class="language-java">compositeSubscription.unsubscribe();</code></pre> <p>这样写法没什么问题,只是每次订阅都要重复这2句</p> <pre> <code class="language-java">Subscription subscription = xxx; compositeSubscription.add(subscription);</code></pre> <p>不是链式了,不好看。<br> 4) 我们的项目里应该有很多公共的部分</p> <pre> <code class="language-java">.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread());</code></pre> <p>比如我的项目里这部分都是一样的,每个请求都写这么长一串也不太好看。</p> <p>直接上代码,看看最终的写法</p> <pre> <code class="language-java">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 } }); } }</code></pre> <pre> <code class="language-java">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); } } }</code></pre> <pre> <code class="language-java"> 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(); } }</code></pre> <pre> <code class="language-java">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; } } }</code></pre> <pre> <code class="language-java"> 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(); } }</code></pre> <p>就这样吧,有兴趣的看看代码,不清楚的或者有不足的地方,欢迎交流。</p> <p>来自:http://www.jianshu.com/p/930d7d728230</p> <p> </p>