Android developerRxAndroid-Rxjava&retrofit&dagger

RxJava Retrofit 封装

2016-07-20  本文已影响1749人  风风风筝

关于绑定生命周期的,推荐我的新设计
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里面

  1. JSON转成Bean

  2. 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的功能呢?

  1. 一般就是在Subscriber的onNext里面去判断,这样的写法满足不了这样的需求:code<0的时候要Retry

  2. Retrofit我们都采用Gson处理返回的数据,如果我返回的结果比较简单,比如根据手机号返回一个验证码{"code":0,msg:"获取验证码成功","data":"8451"},还是要建立一个Bean类,有点麻烦,我想不写这个Bean类,在onNext传入的参数可不可以直接是JSONObject

  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);

不是链式了,不好看。

  1. 我们的项目里应该有很多公共的部分

    .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();
    }
}

源码
https://github.com/iceGeneral/RRCallback

上一篇下一篇

猜你喜欢

热点阅读