淺談 RxAndroid + Retrofit + Databinding

最近 RxAndroid 、MVP、MVVM 一直是 Android 程序猿茶餘飯後的談資,因而我也抱着湊熱鬧的態度試試了試水。這裏就談談試水後的感覺html

什麼是 RxAndroid ?

要說什麼是 RxAndroid ,得從 RxJava 提及。RxJava 在 GitHub 主頁上的自我介紹是 「a library for composing asynchronous and event-based programs using observable sequences for the Java VM」(一個在 Java VM 上使用可觀測的序列來組成異步的、基於事件的程序的庫)。這就是 RxJava ,歸納得很是精準。java

RxJava 的本質能夠壓縮爲異步這一個詞。說到根上,它就是一個實現異步操做的庫,而別的定語都是基於這之上的。android

而RxAndroid是RxJava的一個針對Android平臺的擴展,主要用於 Android 開發。git

什麼是 Retrofit ?

Retrofit 是一套 RESTful 架構的 Android(Java) 客戶端實現,基於註解,提供 JSON to POJO(Plain Ordinary Java Object ,簡單 Java 對象),POJO to JSON,網絡請求(POST,GET, PUT,DELETE 等)封裝。github

既然只是一個網絡請求封裝庫,如今已經有了那麼多的你們已經耳熟能詳的網絡請求封裝庫了,爲何還要介紹它呢,緣由在於 Retrofit 是一套註解形的網絡請求封裝庫,讓咱們的代碼結構更給爲清晰。它能夠直接解析JSON數據變成JAVA對象,甚至支持回調操做,處理不一樣的結果。主要是 Retrofit 能很好的與 RxAndroid 配合使用。segmentfault

想更詳細的瞭解 Retrofit,能夠查看官方文檔api

什麼是 MVVP ?

MVC(Model-View-Controller)和 MVP(Model-View-Presenter)是最多見的軟件架構之一,業界有着普遍應用,你們必定不陌生。但知道什麼事 MVVP 的就很少了,它自己很容易理解,可是要講清楚,這幾個架構的區別就不容易了。緩存

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

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

RxAndroid + Retrofit + Databinding

上面已經分別介紹了 RxAndroid、Retrofit、Databinding ,想必你們也有了個初步的認識,那咱們就看看 RxAndroid + Retrofit + Databinding 產生的「化學反應」。

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

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

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

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

     /**
     * 根據 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 形式從服務器中獲取對應的新聞數據,你們能夠看到代碼的邏輯很是清晰,代碼也很簡潔(這裏使用了 lambda 表達式,不使用的話,代碼會長些,可是邏輯依然清晰),若是是按之前的寫法的話,咱們的代碼會比這複雜的多,還涉及到複雜的線程之間的通訊。而經過 RxJava ,咱們只須要簡單的使用 subscribeOn(Schedulers.io()) 和 observeOn(AndroidSchedulers.mainThread()) 就能夠完成 IO 線程和 UI 線程的切換。

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

總結

優勢:

  1. 代碼邏輯更多加清晰。

  2. 線程之間的切換更加方便、自如。

  3. 代碼可擴展性高,便於維護。

  4. 再也不爲 findViewById() 方法而煩,爲 Activity 減負,總體結構更加清晰。

缺點:

  1. 代碼出錯時,因爲 RxJava 的緣由,將不太容易找到具體出錯位置。

  2. 因爲 RxJava 結構問題,部分須要捕捉的錯誤可能被 RxJava 消化掉。

  3. Databinding 在部分狀況使用不太如意,如 include 進來的 layout 裏對應的 id 不會被關聯起來。

  4. 須要必定的學習成本(固然這不是問題)。

擴展閱讀

  1. RxJava / RxAndroid

  2. Retrofit:

  3. Databinding

相關文章
相關標籤/搜索