Rx Android實戰(重構項目)

jopen 8年前發布 | 19K 次閱讀 Android開發 移動開發 Rx Android

原文  http://hackeris.me/2015/12/20/rxandroid_restructuring/


扯扯

上一次搗蛋 RxAndroid 是今年二月份的事情了,當時 RxAndroid 還處于一個資料甚少交流難的狀態,當時還特意建了一個交流群,讓搞這個的人可以加進來討論討論,畢竟這玩意還是挺有意思的,于是到今天群里已經有 124 人。

在這里我發現了一個現象,進入這個群的小伙伴很多都是中級工程師 or 以上的水準,沒有像很多 XXXXXAndroid 交流群那樣,小白和伸手黨一大堆(在這里沒有任何貶義看待,任何人都是從小白過來,只想說明一個現象)。嗯,分層了,越是接觸新穎的事物、并把事物專研進去的人才會有更大的幾率發現并加入到這個群。就像很多 HR 一樣,從古老的前程無憂到拉勾、周伯通、BOSS 直聘、100offer、github、甚至知乎等等新穎且聚集大量優秀工程師的地方招人。以后 HR 姐姐們也可以到各大框架的討論區去挖人了 [笑哭]。

背景

將要重構的項目是本人的一個業余項目,由于上個公司工作太忙,導致進度緩慢,到現在功能點也還沒完成多少個。趁著這幾天失業,好好追追進度(工作還得要找,畢竟飯還是要吃~),順便重構一下之前考慮不周到 or 不規范的地方,在這里 RxAndroid 充當一個輔助作用,并不是每一處地方都用上場,畢竟具體問題具體分析。

正題

RxAndroid + Retrofit

登陸功能重構之前 (只用 Retrofit 做請求):

public void onLogin(String phone, String psw) {
        mLoginPage.showProgressBar();
        HttpUtils.getApiManager().login(phone, psw)
                .enqueue(new Callback<LoginResponse>() {
                    @Override
                    public void onResponse(Response<LoginResponse> response, Retrofit retrofit) {
                        mLoginPage.hideProgressBar();
                        LoginResponse result = response.body();
                        if(result.error_code == 0){
                            if(saveUserInfo(result)){
                                Oxygen.getInstance().closeAllPopupPage();
                            }else{
                                mLoginPage.showSnackbar(" 未知錯誤 ");
                            }
                        }else{
                            mLoginPage.showSnackbar(result.error_msg);
                        }
                    }

                @Override
                public void onFailure(Throwable t) {
                    mLoginPage.hideProgressBar();
                    handleError(t);
                }
            });

}

private boolean saveUserInfo(LoginResponse response){
    Oxygen.setUserInfo(response.data);
    return Oxygen.getUserInfo().save();
}</pre><br />

流程大概是這樣的:賬號密碼請求服務器 —> 服務器返回用戶資料(此處僅含 accessToken 和 refreshToken)—> 保存用戶資料到本地(文件保存) —> 保存成功則登陸成功,保存失敗則登陸失敗。

可見,我要等 UserInfo.getInstance().save() 的返回來作出判斷登陸成功與否,在這里,我放了在主線程去做,顯然這樣是會有性能問題。

辦法 2:new Thread 去做這個保存,等待返回結果,然后再回到主 Thread 去更新 UI,大概是這樣的:

private void saveUserInfo(final LoginResponse response){
        new Thread(new Runnable() {
            @Override
            public void run() {
                Oxygen.setUserInfo(response.data);
                if(Oxygen.getUserInfo().save()){
                    ((Activity)context).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Oxygen.getInstance().closeAllPopupPage();
                        }
                    });
                }else{
                    ((Activity)context).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mLoginPage.showSnackbar(" 未知錯誤 ");
                        }
                    });
                }
            }
        }).start();
    }

這樣就把卡頓主線程的問題解決了,但~~~ 有沒更直觀、簡單的方法?

RxAndroid !!

public void onLogin(String phone, String psw) {
        HttpUtils.getApiManager().login(phone, psw)
                .subscribeOn(Schedulers.io()) // 請求服務器在 io 線程
                .map(new Func1<LoginResponse, String>() {
                    @Override
                    public String call(LoginResponse response) {
                        if(response.error_code == 0){
                            return saveUserInfo(response) ? "" : " 未知錯誤 ";
                        }else{
                            return  response.error_msg;
                        }
                    }
                })
                .observeOn(AndroidSchedulers.mainThread()) // 指定 doOnSubscribe 在主線程,若沒有 finallyDo 可不加,否則必須加上
                .doOnSubscribe(new Action0() {
                    @Override
                    public void call() {
                        mLoginPage.showProgressBar();
                    }
                })
                .finallyDo(new Action0() {
                    @Override
                    public void call() {
                        mLoginPage.hideProgressBar();
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<String>() {
                    @Override
                    public void call(String result) {
                        if(!TextUtils.isEmpty(result)){
                            mLoginPage.showSnackbar(result);
                        }else{
                            Oxygen.getInstance().closeAllPopupPage();
                        }
                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {
                        handleError(throwable);
                    }
                });
    }

private boolean saveUserInfo(LoginResponse response){
    Oxygen.setUserInfo(response.data);
    return Oxygen.getUserInfo().save();
}</pre><br />

響應式編程,一條鏈式反應,有專門處理請求前、數據返回后處理、請求完成處理、異常等等的函數,還可以給它們特指專門的線程,思路清晰多了。

RxBinding

俺身邊有一位朋友可能由于單身多年,手速達到驚人地步,年輕人嘛,急,按個按鈕總喜歡連續猛按幾下,而頁面也連續彈出幾個。。。

RxAdapterView.itemClickEvents(mListView)
                .throttleFirst(1, TimeUnit.SECONDS)
                .subscribe(new Action1<AdapterViewItemClickEvent>() {
            @Override
            public void call(AdapterViewItemClickEvent adapterViewItemClickEvent) {
                if(mCallback != null){
                    mCallback.onOpenDetailPage(event.position());
                }
            }
        });

throttleFirst 能幫我們解決手速問題,如上。注意,用了 RxBinding 之后,它就不僅僅是一個點擊事件這么簡單了,它成為了一個 Observable,然而我們可以用上它的各種特異功能 duang 的一聲解決問題。

事件總線,RxBus

細心的小伙伴們可能已經發現我的代碼有點奇怪,不太像是在用傳統的開發模式下做操作。對,我是在自己構建的一個 MVC 架構上做的,由于還不很很成熟,就先不放出來討論,例如模塊間的耦合度還是挺大的,所以我想用 RxBus 當事件總線來解耦,這個考慮的東西比較多,先寫到這里,未完待續。


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