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

 

 本文由用戶 oryx 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!