淺談 RxAndroid + Retrofit + Databinding


淺談 RxAndroid + Retrofit + Databinding


最近 RxAndroid 、MVP、MVVM 一直是 Android 程序猿茶余飯后的談資,于是我也抱著湊熱鬧的態(tài)度試試了試水。這里就談談試水后的感受

什么是 RxAndroid ?

要說什么是 RxAndroid ,得從 RxJava 說起。RxJava 在 GitHub 主頁上的自我介紹是 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一個在 Java VM 上使用可觀測的序列來組成異步的、基于事件的程序的庫)。這就是 RxJava ,概括得非常精準。

RxJava 的本質(zhì)可以壓縮為異步這一個詞。說到根上,它就是一個實現(xiàn)異步操作的庫,而別的定語都是基于這之上的。

而RxAndroid是RxJava的一個針對Android平臺的擴展,主要用于 Android 開發(fā)。

什么是 Retrofit ?

Retrofit 是一套 RESTful 架構(gòu)的 Android(Java) 客戶端實現(xiàn),基于注解,提供 JSON to POJO(Plain Ordinary Java Object ,簡單 Java 對象),POJO to JSON,網(wǎng)絡請求(POST,GET, PUT,DELETE 等)封裝。

既然只是一個網(wǎng)絡請求封裝庫,現(xiàn)在已經(jīng)有了那么多的大家已經(jīng)耳熟能詳?shù)木W(wǎng)絡請求封裝庫了,為什么還要介紹它呢,原因在于 Retrofit 是一套注解形的網(wǎng)絡請求封裝庫,讓我們的代碼結(jié)構(gòu)更給為清晰。它可以直接解析JSON數(shù)據(jù)變成JAVA對象,甚至支持回調(diào)操作,處理不同的結(jié)果。主要是 Retrofit 能很好的與 RxAndroid 配合使用。

想更詳細的了解 Retrofit,可以查看官方文檔

什么是 MVVP ?

MVC(Model-View-Controller)和 MVP(Model-View-Presenter)是最常見的軟件架構(gòu)之一,業(yè)界有著廣泛應用,大家一定不陌生。但知道什么事 MVVP 的就不多了,它本身很容易理解,但是要講清楚,這幾個架構(gòu)的區(qū)別就不容易了。

MVVM(Model-View-ViewModel),它采用雙向綁定(data-binding):View的變動,自動反映在 ViewModel,反之亦然。Angular 和 Ember 都采用這種模式。

而 Google 新推出的 Databinding 正是采用了這種模式。

RxAndroid + Retrofit + Databinding

上面已經(jīng)分別介紹了 RxAndroid、Retrofit、Databinding ,想必大家也有了個初步的認識,那我們就看看 RxAndroid + Retrofit + Databinding 產(chǎn)生的“化學反應”。

private void initActionBar() {
    setSupportActionBar(getBinding().toolbar);

    DrawerLayout drawer = getBinding().drawLayout;
    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, getBinding().toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
    drawer.setDrawerListener(toggle);
    toggle.syncState();

    getBinding().navigationView.setNavigationItemSelectedListener(this);
}

代碼中不再充斥著 findViewById 這樣的代碼了,將 etContentView() 換成下面的方法。

this.mBinding = DataBindingUtil.setContentView(context, layout_id);

系統(tǒng)會將我們的 layout 和 data 進行綁定并返回 bind 對象,bind.*** 或者 bind.set 方法來取得控件或修改值。當然還有其它的方法,但是你此時再使用 findViewById() 方法不再有效了。

public interface NewsApi {

    /**
     * 根據(jù) ID 請求新聞列表
     * @param id
     * @return
     */
    @Headers("apikey: 2c61a1cd1f64216e92f7da1603697bf7")
    @GET(ApiConst.NEWS)
    Observable<News.NewsData> queryNewsByID(@Query("channelId") String id, @Query("page") int page);

    /**
     * 根據(jù) ChannelName (標題)請求新聞列表
     * @param title
     * @return
     */
    @Headers("apikey: 2c61a1cd1f64216e92f7da1603697bf7")
    @GET(ApiConst.NEWS)
    Observable<News.NewsData> queryNewsByCName(@Query("channelName") String title, @Query("page") int page);

    /**
     * 根據(jù) title (標題)請求新聞列表
     * @param title
     * @return
     */
    @Headers("apikey: 2c61a1cd1f64216e92f7da1603697bf7")
    @GET(ApiConst.NEWS)
    Observable<News.NewsData> queryNewsByTitle(@Query("title") String title, @Query("page") int page);

}
private void initObservables() {
    Observable.Transformer<List<News>, List<News>> networkingIndicator = RxNetworking.bindRefreshing(getBinding().refresher);

    observableRefresherNewsData = Observable.defer(() -> mNewApi.queryNewsByCName(getArguments().getString(BUNDLE_NAME), 1))
            .doOnUnsubscribe(() -> this.unsubcribe("observableNewsData"))
            .flatMap(data -> Observable.just(data.contentlist))
            .flatMap(list -> getApp().getDB().putList(Const.DB_NEWS_NAME, list, News.class))
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .compose(networkingIndicator);

    observableLoadMoreNewsData = Observable.defer(() -> mNewApi.queryNewsByCName(getArguments().getString(BUNDLE_NAME), mCurrPage + 1))
            .doOnUnsubscribe(() -> this.unsubcribe("observableNewsData"))
            .map(data -> {
                mCurrPage = data.currentPage;
                return data.contentlist;
            })
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .compose(networkingIndicator);

    // 刷新/加載更多
    RxSwipeRefreshLayout.refreshes(getBinding().refresher)
            .doOnUnsubscribe(() -> this.unsubcribe("SwipeRefreshLayout"))
            .flatMap(avoid -> observableRefresherNewsData)
            .compose(bindToLifecycle())
            .subscribe(RxList.prependTo(mNews, getBinding().content), this::showError);

    RxEndlessRecyclerView.reachesEnd(getBinding().content)
            .doOnUnsubscribe(() -> this.unsubcribe("Recycler"))
            .flatMap(avoid -> observableLoadMoreNewsData)
            .compose(bindToLifecycle())
            .subscribe(RxList.appendTo(mNews), this::showError);

    // 首次進入手動加載
    observableRefresherNewsData
            .map(list -> {
                mNews.clear();
                return list;
            })
            .compose(bindToLifecycle())
            .subscribe(RxList.prependTo(mNews, getBinding().content), this::showError);

}

上面代碼是使用 Retrofit 以 Get 形式從服務器中獲取對應的新聞數(shù)據(jù),大家可以看到代碼的邏輯非常清晰,代碼也很簡潔(這里使用了 lambda 表達式,不使用的話,代碼會長些,但是邏輯依然清晰),如果是按以前的寫法的話,我們的代碼會比這復雜的多,還涉及到復雜的線程之間的通信。而通過 RxJava ,我們只需要簡單的使用 subscribeOn(Schedulers.io()) 和 observeOn(AndroidSchedulers.mainThread()) 就可以完成 IO 線程和 UI 線程的切換。

帥的簡直不敢相信,原來還可以這樣玩。

總結(jié)

優(yōu)點:

  1. 代碼邏輯更多加清晰。
  2. 線程之間的切換更加方便、自如。
  3. 代碼可擴展性高,便于維護。
  4. 不再為 findViewById() 方法而煩,為 Activity 減負,整體結(jié)構(gòu)更加清晰。

缺點:

  1. 代碼出錯時,由于 RxJava 的原因,將不太容易找到具體出錯位置。
  2. 由于 RxJava 結(jié)構(gòu)問題,部分需要捕捉的錯誤可能被 RxJava 消化掉。
  3. Databinding 在部分情況使用不太如意,如 include 進來的 layout 里對應的 id 不會被關聯(lián)起來。
  4. 需要一定的學習成本(當然這不是問題)。

廣告

這里打個小廣告,介紹下我最近開發(fā)的幾個小應用

小白球
私人訂制
輕截

大家多支持下,如果下載達到 1000 的話,我會將其中一兩個項目開源出來的哦。

擴展閱讀

  1. RxJava / RxAndroid
  1. Retrofit:
  1. Databinding

其它

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,278評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,241評論 2 375

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