浅谈 RxAndroid + Retrofit + Databinding


浅谈 RxAndroid + Retrofit + Databinding


最近 RxAndroid 、MVP、MVVM 一直是 Android 程序猿茶余饭后的谈资,于是我也抱着凑热闹的态度试试了试水。这里就谈谈试水后的感受

什么是 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 的本质可以压缩为异步这一个词。说到根上,它就是一个实现异步操作的库,而别的定语都是基于这之上的。

而RxAndroid是RxJava的一个针对Android平台的扩展,主要用于 Android 开发。

什么是 Retrofit ?

Retrofit 是一套 RESTful 架构的 Android(Java) 客户端实现,基于注解,提供 JSON to POJO(Plain Ordinary Java Object ,简单 Java 对象),POJO to JSON,网络请求(POST,GET, PUT,DELETE 等)封装。

既然只是一个网络请求封装库,现在已经有了那么多的大家已经耳熟能详的网络请求封装库了,为什么还要介绍它呢,原因在于 Retrofit 是一套注解形的网络请求封装库,让我们的代码结构更给为清晰。它可以直接解析JSON数据变成JAVA对象,甚至支持回调操作,处理不同的结果。主要是 Retrofit 能很好的与 RxAndroid 配合使用。

想更详细的了解 Retrofit,可以查看官方文档

什么是 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. 需要一定的学习成本(当然这不是问题)。

广告

这里打个小广告,介绍下我最近开发的几个小应用

小白球
私人订制
轻截

大家多支持下,如果下载达到 1000 的话,我会将其中一两个项目开源出来的哦。

扩展阅读

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

其它

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,951评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,606评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,601评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,478评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,565评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,587评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,590评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,337评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,785评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,096评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,273评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,935评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,578评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,199评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,440评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,163评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,133评论 2 352

推荐阅读更多精彩内容