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>