淺談 RxAndroid + Retrofit + Databinding
最近 RxAndroid 、MVP、MVVM 一直是 Android 程序猿茶余飯后的談資,于是我也抱著湊熱鬧的態(tài)度試試了試水。這里就談?wù)勗囁蟮母惺?/p>
什么是 RxAndroid ?
要說(shuō)什么是 RxAndroid ,得從 RxJava 說(shuō)起。RxJava 在 GitHub 主頁(yè)上的自我介紹是 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一個(gè)在 Java VM 上使用可觀測(cè)的序列來(lái)組成異步的、基于事件的程序的庫(kù))。這就是 RxJava ,概括得非常精準(zhǔn)。
RxJava 的本質(zhì)可以壓縮為異步這一個(gè)詞。說(shuō)到根上,它就是一個(gè)實(shí)現(xiàn)異步操作的庫(kù),而別的定語(yǔ)都是基于這之上的。
而RxAndroid是RxJava的一個(gè)針對(duì)Android平臺(tái)的擴(kuò)展,主要用于 Android 開(kāi)發(fā)。
什么是 Retrofit ?
Retrofit 是一套 RESTful 架構(gòu)的 Android(Java) 客戶端實(shí)現(xiàn),基于注解,提供 JSON to POJO(Plain Ordinary Java Object ,簡(jiǎn)單 Java 對(duì)象),POJO to JSON,網(wǎng)絡(luò)請(qǐng)求(POST,GET, PUT,DELETE 等)封裝。
既然只是一個(gè)網(wǎng)絡(luò)請(qǐng)求封裝庫(kù),現(xiàn)在已經(jīng)有了那么多的大家已經(jīng)耳熟能詳?shù)木W(wǎng)絡(luò)請(qǐng)求封裝庫(kù)了,為什么還要介紹它呢,原因在于 Retrofit 是一套注解形的網(wǎng)絡(luò)請(qǐng)求封裝庫(kù),讓我們的代碼結(jié)構(gòu)更給為清晰。它可以直接解析JSON數(shù)據(jù)變成JAVA對(duì)象,甚至支持回調(diào)操作,處理不同的結(jié)果。主要是 Retrofit 能很好的與 RxAndroid 配合使用。
想更詳細(xì)的了解 Retrofit,可以查看官方文檔
什么是 MVVP ?
MVC(Model-View-Controller)和 MVP(Model-View-Presenter)是最常見(jiàn)的軟件架構(gòu)之一,業(yè)界有著廣泛應(yīng)用,大家一定不陌生。但知道什么事 MVVP 的就不多了,它本身很容易理解,但是要講清楚,這幾個(gè)架構(gòu)的區(qū)別就不容易了。
MVVM(Model-View-ViewModel),它采用雙向綁定(data-binding):View的變動(dòng),自動(dòng)反映在 ViewModel,反之亦然。Angular 和 Ember 都采用這種模式。
而 Google 新推出的 Databinding 正是采用了這種模式。
RxAndroid + Retrofit + Databinding
上面已經(jīng)分別介紹了 RxAndroid、Retrofit、Databinding ,想必大家也有了個(gè)初步的認(rèn)識(shí),那我們就看看 RxAndroid + Retrofit + Databinding 產(chǎn)生的“化學(xué)反應(yīng)”。
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)會(huì)將我們的 layout 和 data 進(jìn)行綁定并返回 bind 對(duì)象,bind.*** 或者 bind.set 方法來(lái)取得控件或修改值。當(dāng)然還有其它的方法,但是你此時(shí)再使用 findViewById() 方法不再有效了。
public interface NewsApi {
/**
* 根據(jù) ID 請(qǐng)求新聞列表
* @param id
* @return
*/
@Headers("apikey: 2c61a1cd1f64216e92f7da1603697bf7")
@GET(ApiConst.NEWS)
Observable<News.NewsData> queryNewsByID(@Query("channelId") String id, @Query("page") int page);
/**
* 根據(jù) ChannelName (標(biāo)題)請(qǐng)求新聞列表
* @param title
* @return
*/
@Headers("apikey: 2c61a1cd1f64216e92f7da1603697bf7")
@GET(ApiConst.NEWS)
Observable<News.NewsData> queryNewsByCName(@Query("channelName") String title, @Query("page") int page);
/**
* 根據(jù) title (標(biāo)題)請(qǐng)求新聞列表
* @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);
// 首次進(jìn)入手動(dòng)加載
observableRefresherNewsData
.map(list -> {
mNews.clear();
return list;
})
.compose(bindToLifecycle())
.subscribe(RxList.prependTo(mNews, getBinding().content), this::showError);
}
上面代碼是使用 Retrofit 以 Get 形式從服務(wù)器中獲取對(duì)應(yīng)的新聞數(shù)據(jù),大家可以看到代碼的邏輯非常清晰,代碼也很簡(jiǎn)潔(這里使用了 lambda 表達(dá)式,不使用的話,代碼會(huì)長(zhǎng)些,但是邏輯依然清晰),如果是按以前的寫(xiě)法的話,我們的代碼會(huì)比這復(fù)雜的多,還涉及到復(fù)雜的線程之間的通信。而通過(guò) RxJava ,我們只需要簡(jiǎn)單的使用 subscribeOn(Schedulers.io()) 和 observeOn(AndroidSchedulers.mainThread()) 就可以完成 IO 線程和 UI 線程的切換。
帥的簡(jiǎn)直不敢相信,原來(lái)還可以這樣玩。
總結(jié)
優(yōu)點(diǎn):
- 代碼邏輯更多加清晰。
- 線程之間的切換更加方便、自如。
- 代碼可擴(kuò)展性高,便于維護(hù)。
- 不再為 findViewById() 方法而煩,為 Activity 減負(fù),整體結(jié)構(gòu)更加清晰。
缺點(diǎn):
- 代碼出錯(cuò)時(shí),由于 RxJava 的原因,將不太容易找到具體出錯(cuò)位置。
- 由于 RxJava 結(jié)構(gòu)問(wèn)題,部分需要捕捉的錯(cuò)誤可能被 RxJava 消化掉。
- Databinding 在部分情況使用不太如意,如 include 進(jìn)來(lái)的 layout 里對(duì)應(yīng)的 id 不會(huì)被關(guān)聯(lián)起來(lái)。
- 需要一定的學(xué)習(xí)成本(當(dāng)然這不是問(wèn)題)。
廣告
這里打個(gè)小廣告,介紹下我最近開(kāi)發(fā)的幾個(gè)小應(yīng)用



大家多支持下,如果下載達(dá)到 1000 的話,我會(huì)將其中一兩個(gè)項(xiàng)目開(kāi)源出來(lái)的哦。
擴(kuò)展閱讀
- RxJava / RxAndroid
- 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
其它
- Blog:http://imli.me
- Github:https://github.com/iQuick