【问题标题】:Android repository-pattern & RxJava: Use Flowable or Single?Android 存储库模式和 RxJava:使用 Flowable 还是 Single?
【发布时间】:2018-02-17 09:59:30
【问题描述】:

最近,我了解到在设计应用程序的后端(存储库,而不是服务器端后端)时,拥有单一真相 (SSOT) 是多么重要。 https://developer.android.com/topic/libraries/architecture/guide.html

通过开发一个新闻提要应用程序(使用了不起的https://newsapi.org/),我试图了解有关应用程序架构的更多信息。 但是,我不确定如何为我的应用程序设计存储库界面。 顺便说一句:我正在使用 MVVM 作为我的表示层。 View 订阅 ViewModel 的 LiveData。 ViewModel 订阅 RxJava 流。

所以我想出了两种方法:

方法一:

interface NewsFeedRepository {
        fun loadFeed(): Flowable<List<Article>>
        fun refreshFeed(): Completable
        fun loadMore(): Completable
    }

interface SearchArticleRepository {
    fun searchArticles(sources: List<NewsSource>? = null, query: String? = null): Flowable<List<Article>>
    fun moreArticles(): Completable
}

interface BookmarkRepository {
    fun getBookmarkedArticles(): Flowable<List<Article>>
    fun bookmarkArticle(id: String): Completable
}

这种方法主要使用 Flowables,如果底层 SSOT(数据库)中的相应数据发生变化(例如,旧数据被 API 中的新数据替换,更多数据从 API 中加载,......),就会发出数据。但是,我不确定为 SearchArticleRepository#searchArticles(...) 使用 Flowable 是否有意义。因为它就像一些请求/响应的事情,也许 Single 可能会让我更直观。

方法二:

interface NewsFeedRepository {
    fun loadFeed(): Single<List<Article>>
    fun refreshFeed(): Single<List<Article>>
    fun loadMore(): Single<List<Article>>
}

interface SearchArticleRepository {
    fun searchArticles(sources: List<NewsSource>? = null, query: String? = null): Single<List<Article>>
    fun moreArticles(): Single<List<Article>>​
}

interface BookmarkRepository {
    fun getBookmarkedArticles(): Single<List<Article>>
    fun bookmarkArticle(id: String): Single<Article> // Returns the article that was modified. Articles are immutable. ​
}

这种方法使用 Singles 而不是 Flowables。这看起来很直观,但如果 SSOT 中的数据发生变化,则不会发出任何变化。相反,必须再次调用存储库。要考虑的另一个方面是 ViewModel 可能必须管理自己的状态。 让我们以FeedViewModel 为例(伪代码)。

class FeedViewModel : ViewModel() {
    // Variables, Boilerplate, ...
    val newsFeed: LiveData<List<Article>>
    private val articles = mutableListOf<Article>()

    fun loadNewsFeed() {
        // ...
        repository.loadFeed()
                   //...
                   // On success, clear the feed and append the loaded articles.
                  .subscribe({articles.clear(); articles.addAll(it)})
        // ...
    } 

    fun loadMore() {
        // ...
        repository.loadMore()
                   //...
                   // On success, append the newly loaded articles to the feed.
                  .subscribe({articles.addAll(it)}) 
        // ...
    }
}

因此,这对于像我这样的较小应用程序可能并不重要,但对于较大的应用程序肯定会出现问题(请参阅状态管理:http://hannesdorfmann.com/android/arch-components-purist)。

最后,我想知道采用哪种方法以及为什么。 有最佳实践吗?我知道你们中的许多人已经完成了一些更大的软件项目/应用程序,如果你们中的一些人可以与我和其他人分享一些知识,那就太棒了。

非常感谢!

【问题讨论】:

    标签: android kotlin rx-java2


    【解决方案1】:

    在您的情况下,我宁愿选择使用Observables 而不是Flowables 的第一种方法:

    interface NewsFeedRepository {
        fun loadFeed(): Observable<List<Article>>
        fun refreshFeed(): Completable
        fun loadMore(): Completable
    }
    
    interface SearchArticleRepository {
        fun searchArticles(sources: List<NewsSource>? = null, query: String? = null): Observable<List<Article>>
        fun moreArticles(): Completable
    }
    
    interface BookmarkRepository {
        fun getBookmarkedArticles(): Observable<List<Article>>
        fun bookmarkArticle(id: String): Completable
    }
    

    我认为您没有任何理由必须为此使用Flowable,因为您在检查存储库更改时永远不会遇到任何与 OOME 相关的问题。换句话说,对于您的用例恕我直言,背压根本不需要。

    检查this official guide,它会告诉我们何时使用Flowable 而不是Observable

    另一方面,与问题本身无关,我严重怀疑loadMoremoreArticles 方法的目的是什么,因为它们返回Completable。在不了解上下文的情况下,您似乎可以用更好的名称重构方法名称,或者如果他们按照名称执行的操作更改返回类型。

    【讨论】:

    • 是的,你可能已经猜到了,我最近开始学习 RxJava 2,所以我很感谢你关于使用 Observable 而不是 Flowable 的建议。关于您的疑问:loadMore 尝试从 API 获取更多提要文章,然后将它们插入数据库,这将触发来自 loadFeed 的 Observable,从而导致视图重新呈现。如果该过程失败,Completable 将失败(视图显示错误)。 moreArticles 工作原理基本相同,并从 API(分页)请求更多文章。但是,我不确定如何以不同的方式解决这个问题?有什么建议吗?
    • 我的思考过程:当用户向下滚动提要回收器视图时,ScrollListener 触发 FeedViewModel#loadMore 调用 FeedRepository#loadMore。如果成功,视图将自动(在上面的评论中描述)显示附加数据。如果加载更多提要数据失败,视图会显示错误并在回收器视图底部呈现重试按钮。如果没有这个 Completable:我怎么知道加载更多提要数据是成功还是失败?
    • 我们将在这里讨论另一个问题,应该更好地按照 StackOverflow 规则在另一个问题中解决。无论如何,即使您在存储库中混合 API 和 DB 后端调用,调用也应该清楚它在做什么。 loadMore 在我看来不应该只是调用 API,然后根据该调用存储触发任何其他事情。该责任应该在另一点上承担,API 应该从 API 或 DB 返回提要。检查这个干净的架构设计模式指南:github.com/android10/Android-CleanArchitectureUserDataRepository.javaclass
    • 所以你的意思是存储库本身应该使用某种抽象 api 来负责同步网络 api 和 db?无论如何,我将就这个问题提出一个新问题。谢谢你的帮助:)
    【解决方案2】:

    我相信第一种方法更好,您的存储库会在数据更改时更新数据,并且您的视图模型会自动收到通知,这很酷,而在第二种方法中,您必须再次调用存储库,这不是真的反应式编程。

    另外,假设数据可以被某些东西改变,而不是从视图中加载更多事件,比如当新数据添加到服务器时,或者应用程序的其他部分改变了数据,现在在第一种方法中你再次获得数据自动而第二次您甚至不知道更改的数据,并且您不知道何时再次调用该方法。

    【讨论】:

    • 好的,我已经假设这会是更好的方法。但是刷新提要或加载更多提要数据怎么样?你认为 Completable 适合吗?还是有不同的方法来解决这个问题?
    猜你喜欢
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多