RxJava:封裝和使用

聲明:本文只講封裝沿猜,基本用法請(qǐng)參考官方文檔或者其他文章~

0. 依賴(lài)

2.0已出枚荣,但是暫時(shí)還沒(méi)有來(lái)得及去看;所以還是那目前項(xiàng)目中在用的來(lái)講吧啼肩。

    compile 'io.reactivex:rxjava:1.2.4'
    compile 'io.reactivex:rxandroid:1.2.1'
    compile 'com.artemzin.rxjava:proguard-rules:1.2.7.0'
    compile 'com.trello:rxlifecycle:1.0'
    compile 'com.trello:rxlifecycle-components:1.0'
    compile 'com.jakewharton.rxbinding:rxbinding:1.0.0'

以下展示目前項(xiàng)目中最常用的幾個(gè)例子:

1. View相關(guān)

點(diǎn)擊事件
  • 使用
Rx.clicks(mBtn, this, v -> doAction());
  • 功能點(diǎn)
  1. 過(guò)濾500ms內(nèi)的重復(fù)點(diǎn)擊橄妆;
  2. 綁定當(dāng)前activity或者fragment的生命周期,避免內(nèi)存泄漏祈坠;
  • 封裝
    //view點(diǎn)擊事件害碾,500毫秒過(guò)濾重復(fù)點(diǎn)擊
    public static void clicks(View view, BaseActivity activity, final Action1<Void> onNext) {
        RxView.clicks(view)
                .throttleFirst(500, TimeUnit.MILLISECONDS)
                .compose(activity.bindToLifecycle())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(onNext, e -> e.printStackTrace());
    }
輸入框文字變動(dòng)
  • 使用
Rx.afterTextChangeEvents(this, mEditText, event -> doAction());
  • 功能點(diǎn)
  1. 過(guò)濾500ms內(nèi)的請(qǐng)求,尤其是當(dāng)輸入框文字變動(dòng)后需要進(jìn)行網(wǎng)絡(luò)請(qǐng)求時(shí)赦拘,可以有效避免產(chǎn)生大量請(qǐng)求慌随;
  2. 綁定當(dāng)前activity或者fragment的生命周期,避免內(nèi)存泄漏躺同;
  • 封裝
    //TextView watcher阁猜,間隔500毫秒
    public static void afterTextChangeEvents(BaseActivity activity, TextView textView, Action1<TextViewAfterTextChangeEvent> onNext) {
        RxTextView.afterTextChangeEvents(textView)
                .throttleLast(500, TimeUnit.MILLISECONDS)
                .compose(activity.bindToLifecycle())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(onNext, e -> e.printStackTrace());
    }

2. 網(wǎng)絡(luò)請(qǐng)求(劃重點(diǎn)!)

  • 使用
 API.uploadImg(picInfo) //以上傳圖片為例
                .compose(RxTransformers.doApi(this))
                .subscribe((Result result) -> {
                    //to do

                });

簡(jiǎn)潔到爆炸有木有!K褡选蹦漠!

  • 功能點(diǎn)
  1. 保留原有的鏈?zhǔn)秸{(diào)用方式;
  2. subscribe()中可以?xún)H僅傳入 onNext车海,onErroronComplete可選笛园,所以再加上lamda加持,做到代碼最簡(jiǎn)潔~(原先若只傳入onNext而不傳入onError,當(dāng)網(wǎng)絡(luò)異呈讨ィ或者onNext執(zhí)行發(fā)生異常時(shí)研铆,會(huì)拋出OnErrorNotImplementedException);
  3. 線(xiàn)程切換州叠;
  4. 綁定當(dāng)前頁(yè)面生命周期棵红,當(dāng)onPause時(shí),停止未完成的請(qǐng)求(拋掉已經(jīng)請(qǐng)求來(lái)的response咧栗,不進(jìn)行處理)逆甜;
  5. 請(qǐng)求結(jié)果統(tǒng)一預(yù)處理:
    5.1 網(wǎng)絡(luò)異常處理與上報(bào)虱肄;
    5.2 接口請(qǐng)求錯(cuò)誤信息展示;
    5.3 loading UI的顯示和隱藏交煞;
    5.4 token失效后的統(tǒng)一處理咏窿;
  • 封裝
    若想理解以下封裝原理,請(qǐng)先通讀compose()lift()兩個(gè)操作符的源碼素征;
API.uploadImg(picInfo)是基于retrofit的封裝

此處省略

compose()操作符中傳入的自定義Transformer
public class RxTransformers {

    public static <T> Observable.Transformer<T, T> io_main() {
        return (Observable<T> observable) -> observable
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }

    public static <T> Observable.Transformer<T, T> doApi(BaseActivity activity, HttpResultInterceptor.Type type) {
        return (Observable<T> observable) -> observable
                .compose(io_main())//線(xiàn)程切換
                .compose(activity.bindToLifecycle())//生命周期
                .lift(HttpResultInterceptor.get(activity, type));//請(qǐng)求結(jié)果預(yù)處理
    }

    public static <T> Observable.Transformer<T, T> doApi(BaseActivity activity) {
        return doApi(activity, HttpResultInterceptor.Type.ALL);
    }

}

線(xiàn)程切換和頁(yè)面生命周期綁定沒(méi)啥好講的集嵌,重點(diǎn)是lift()中傳入的自定義operator

lift(HttpResultInterceptor.get(activity, type));

請(qǐng)求結(jié)果預(yù)處理

/**
 * Created by Jessewo on 2017/7/25.
 * <p>
 * 1.API鏈?zhǔn)秸{(diào)用
 * 2.lamda表達(dá)式(第一層subscriber,可以只實(shí)現(xiàn)onNext(),onError/onComplete非必須)
 */

public class HttpResultInterceptor {

    private static final String TAG = "HttpResultInterceptor";

    public enum Type {
        /**
         * 1. filter exception and show exception msg when onError<br/>
         * 2. show loading when onStart, and hide when onComplete or onError<br/>
         * 3. show error message when onNext
         */
        ALL,
        /**
         * 1. filter exception and show exception msg when onError<br/>
         * 2. show error message when onNext
         */
        ERROR_MSG,
        /**
         * 1. filter exception and show exception msg when onError<br/>
         * 2. show loading when onStart, and hide when onComplete or onError
         */
        LOADING,
        /**
         * 1. filter exception and show exception msg when onError
         */
        NONE
    }

    public static Observable.Operator get(HttpResultHandler handler, Type type) {
        return new OperatorHttpResult(handler, type);
    }

    private static class OperatorHttpResult<T> implements Observable.Operator<T, T>, Subscription {

        private SoftReference<HttpResultHandler> mHandler;

        private Type mType;

        OperatorHttpResult(HttpResultHandler httpResultHandler) {
            mHandler = new SoftReference<>(httpResultHandler);
        }

        OperatorHttpResult(HttpResultHandler httpResultHandler, Type type) {
            mHandler = new SoftReference<>(httpResultHandler);
            mType = type;
        }

        @Override
        public Subscriber<? super T> call(Subscriber<? super T> subscriber) {
            HttpResultSubscriber<? super T> parent = new HttpResultSubscriber<>(subscriber, mHandler, mType);
            //parent subscriber 獨(dú)立控制 unSubscribe行為
            parent.add(this);
            return parent;
        }

        @Override
        public void unsubscribe() {
            mHandler.clear();
        }

        @Override
        public boolean isUnsubscribed() {
            return mHandler.get() == null;
        }
    }

    /**
     * 異常處理原則與safeSubscriber稍有不同:<br/>
     * 1. onNext所有異常抓取->onError;(同)<br/>
     * 2. onComplete所有異常拋出;(同)<br/>
     * 3. onError拋出除了OnErrorNotImplementedException之外的所有異常;(不同)
     */
    private static class HttpResultSubscriber<T> extends Subscriber<T> {

        private Subscriber<? super T> mChild;
        private SoftReference<HttpResultHandler> mHandler;
        private boolean showProgress;
        private boolean showError;

        private boolean done;

        HttpResultSubscriber(Subscriber<? super T> child,
                             SoftReference<HttpResultHandler> preHandler,
                             Type type) {
            mChild = child;
            mHandler = preHandler;
            switch (type) {
                case ERROR_MSG:
                    showProgress = false;
                    showError = true;
                    break;
                case LOADING:
                    showProgress = true;
                    showError = false;
                    break;
                case NONE:
                    showProgress = false;
                    showError = false;
                    break;
                default://all
                    showProgress = true;
                    showError = true;
                    break;
            }
        }

        @Override
        public void onStart() {
            //main thread
            showProgress();
        }

        @Override
        public void onCompleted() {
            if (done || isUnsubscribed() || mChild.isUnsubscribed())
                return;
            done = true;
            try {
                dismissProgress();
                mChild.onCompleted();
            } finally {
                try {
                    unsubscribe();
                } catch (Throwable e) {
                    RxJavaHooks.onError(e);
                    throw new OnCompletedFailedException(e.getMessage(), e);
                }
            }
        }

        @Override
        public void onError(Throwable e) {
            if (done || isUnsubscribed() || mChild.isUnsubscribed())
                return;
            done = true;
            try {
                dismissProgress();

                if (e instanceof OnErrorNotImplementedException) {
                    e = e.getCause();
                }
                e.printStackTrace();
                String error = App.getInstance().getString(R.string.error_network);
                if (e instanceof SocketTimeoutException) {
                    ToastUtil.show(error + "(" + NETWORK_ERROR_TIMEOUT + ")");
                } else if (e instanceof ConnectException) {
                    ToastUtil.show(error + "(" + NETWORK_ERROR_INTERRUPTION + ")");
                } else if (e instanceof UnknownHostException
                        || (!TextUtils.isEmpty(e.getMessage()) && e.getMessage().contains("No address associated with hostname"))) {
                    ToastUtil.show(error + "(" + NETWORK_ERROR_UNKNOWN_HOST + ")");
                } else {
                    ToastUtil.show(error + "(" + NETWORK_ERROR_UNKNOWN + ")");
                    Analytics.getInstance().onError(e);//對(duì)于非常規(guī)異常上報(bào)后臺(tái)監(jiān)控
                }

                mChild.onError(e);
            } catch (OnErrorNotImplementedException e2) {
                //ignore
                LOG.d(TAG, "onError: OnErrorNotImplementedException");
            } finally {
                try {
                    unsubscribe();
                } catch (Throwable e3) {
                    RxJavaHooks.onError(e3);
                    throw new OnErrorFailedException(e3.getMessage(), e3);
                }
            }
        }

        @Override
        public void onNext(T t) {
            if (done || isUnsubscribed() || mChild.isUnsubscribed())
                return;
            try {
                if (showError) {
                    if (t instanceof Result) {
                        Result result = (Result) t;
                        checkResult(result);
                    } else if (t instanceof MixResult) {
                        MixResult mixResult = (MixResult) t;
                        Result result1 = mixResult.getResult1();
                        Result result2 = mixResult.getResult2();
                        if (result1 != null && result2 != null) {
                            checkResult(result1);
                            checkResult(result2);
                        }
                    }
                }
                mChild.onNext(t);
            } catch (Throwable e) {
                onError(e);
            }
        }

        private void checkResult(Result result) {
            int status = result.getStatus();
            switch (status) {
                case API.SUCCESS_CODE:
                case API.SKIP_CODE:
                    break;
                case API.RELOGIN_CODE:
                    //token失效
                    String msg = result.getMsg();
                    HttpResultHandler preHandler = mHandler.get();
                    if (preHandler != null) {
                        preHandler.onTokenInvalid(msg);
                    } else {
                        ToastUtil.show(msg);
                    }
                    break;
                default:
                    ErrorMsg.getInstance().show(result);
                    break;
            }
        }

        private void showProgress() {
            if (showProgress && mHandler.get() != null)
                mHandler.get().showProgress();
        }

        private void dismissProgress() {
            if (showProgress && mHandler.get() != null)
                mHandler.get().dismissProgress();
        }
    }

    //BaseActivity或者BaseFragment需要實(shí)現(xiàn)此接口
    public interface HttpResultHandler {

        void showProgress();

        void dismissProgress();

        void onTokenInvalid(String msg);

        //其他功能可擴(kuò)展
        //        void showMessage(String msg);
        //        void lowerVersion();
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市御毅,隨后出現(xiàn)的幾起案子根欧,更是在濱河造成了極大的恐慌,老刑警劉巖端蛆,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凤粗,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡欺税,警方通過(guò)查閱死者的電腦和手機(jī)侈沪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)晚凿,“玉大人亭罪,你說(shuō)我怎么就攤上這事〖呋啵” “怎么了应役?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)燥筷。 經(jīng)常有香客問(wèn)我箩祥,道長(zhǎng),這世上最難降的妖魔是什么肆氓? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任袍祖,我火速辦了婚禮,結(jié)果婚禮上谢揪,老公的妹妹穿的比我還像新娘蕉陋。我一直安慰自己,他們只是感情好拨扶,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布凳鬓。 她就那樣靜靜地躺著,像睡著了一般患民。 火紅的嫁衣襯著肌膚如雪缩举。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,462評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音仅孩,去河邊找鬼托猩。 笑死,一個(gè)胖子當(dāng)著我的面吹牛杠氢,可吹牛的內(nèi)容都是我干的站刑。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼鼻百,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了摆尝?” 一聲冷哼從身側(cè)響起温艇,我...
    開(kāi)封第一講書(shū)人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎堕汞,沒(méi)想到半個(gè)月后勺爱,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡讯检,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年琐鲁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片人灼。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡围段,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出投放,到底是詐尸還是另有隱情奈泪,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布灸芳,位于F島的核電站涝桅,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏烙样。R本人自食惡果不足惜冯遂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望谒获。 院中可真熱鬧蛤肌,春花似錦、人聲如沸究反。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)精耐。三九已至狼速,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間卦停,已是汗流浹背向胡。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工恼蓬, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人僵芹。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓处硬,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親拇派。 傳聞我的和親對(duì)象是個(gè)殘疾皇子荷辕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容