最近 RxAndroid 、MVP、MVVM 一直是 Android 程序猿茶餘飯後的談資,因而我也抱着湊熱鬧的態度試試了試水。這裏就談談試水後的感覺html
要說什麼是 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 是一套 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
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 產生的「化學反應」。
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 線程的切換。
帥的簡直不敢相信,原來還能夠這樣玩。
代碼邏輯更多加清晰。
線程之間的切換更加方便、自如。
代碼可擴展性高,便於維護。
再也不爲 findViewById() 方法而煩,爲 Activity 減負,總體結構更加清晰。
代碼出錯時,因爲 RxJava 的緣由,將不太容易找到具體出錯位置。
因爲 RxJava 結構問題,部分須要捕捉的錯誤可能被 RxJava 消化掉。
Databinding 在部分狀況使用不太如意,如 include 進來的 layout 裏對應的 id 不會被關聯起來。
須要必定的學習成本(固然這不是問題)。
RxJava / RxAndroid
極力推薦的代碼家乾貨:http://gank.io/post/560e15be2dca930e00da1083
Retrofit:
Retrofit 官方文檔:http://square.github.io/retrofit/
Retrofit 使用介紹:http://www.cnblogs.com/angeldevil/p/3757335.html
Retrofit 離線緩存策略:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0115/3873.html
Databinding
MVC,MVP 和 MVVM 的圖示:http://www.ruanyifeng.com/blog/2015/02/mvcmvp_mvvm.html
DataBinding 用戶指南:http://segmentfault.com/a/1190000002876984
Github 上比較全面的:https://github.com/LyndonChin/MasteringAndroidDataBinding