【问题标题】:LiveData with two sources?LiveData 有两个来源?
【发布时间】:2020-05-14 18:36:46
【问题描述】:

我想对不同来源使用相同的 LiveData。一个来自 API 调用,它是可观察的,另一个来自数据库,它是 LiveData。我希望能够做这样的事情:

private LiveData<List<Items>> items = new MutableLiveData<>();

// this one comes from an API and it's an observable
public void onApiItemsSelected(String name) {
 Disposable disposable = repository.getItemsFromApi(name)
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe(itemsList -> {
     items.setValue(itemsList);
 });

 compositeDisposable.add(disposable);
}

// this one comes from the database and it's a livedata that I want to transform first
public void onDatabaseItemsSelected() {
 items = Transformations.map(repository.getItemsFromdatabase(), itemsList -> {
    List<Items> finalItemsList = new ArrayList<>();

    for (Item item : itemsList) {
      finalItemsList.add(itemsList.toSomething());
    }

    return finalItemsList;
 });
}

问题是 Transformation.map 总是返回一个 LiveData 并且为了使 items.setValue(itemsList) 项目需要是一个 MutableLiveData。我尝试使用 MediatorLiveData,但它调用了两个源并混合了所有内容。这不是我在这里需要的。我需要一个来源或另一个。有可能吗?

【问题讨论】:

  • 我不确定你想做什么。您想合并这些结果还是想访问最新的结果(例如最新的数据库或 api)?
  • 如果用户单击调用 onApiItemsSelected 的按钮,我想显示 API 中的值,如果他单击 onDatabaseItemsSelected,我想显示数据库中的值。我不希望它们结合在一起。我想显示其中一个。它们是相同的对象,将显示在同一个列表中。只有来源不同。
  • 我会尝试发布代码来提供帮助。
  • 为什么不总是将 API 中的数据保存到 DB 中,而只显示 DB 中的数据?

标签: android android-livedata android-architecture-components mutablelivedata mediatorlivedata


【解决方案1】:

我认为您可以将此责任委托给您的存储库。

请参阅此open source project 和相关article 以了解更多信息。

基本上,存储库处理决定从哪个源获取数据的所有复杂性(请参阅JobRepository)。它将 rx Observable 暴露给 ViewModel(参见 JobsViewModel)。然后 ViewModel 所要做的就是更新LiveData

private val presentation: MutableLiveData<Presentation> = MutableLiveData()

fun bind() {
        jobRepository.getJobs(true)
            .subscribeOn(Schedulers.io())
            .subscribe { resource: Resource<List<JobWithRelations>> ->
                when (resource) {
                    // commenting some parts for brevity ...
                    is Resource.ResourceFound -> {
                        presentation.postValue(Presentation(getApplication(), resource.data!!))
                    }
                    // ...
                }
            }.addTo(compositeDisposable)
    }

管理多个数据源很复杂。在构建此架构涉及的许多方面中,需要牢记的事项包括:

  • 内存缓存过期策略
  • 数据库缓存策略,例如,当用户离线时
  • API 调用的限制和重试
  • 当新数据来自网络时更新内存缓存或数据库的策略

Dropbox Store 是一个可以帮助您的库。有了它,您可以从他们的文档中构建您的数据源,如下例所示:

StoreBuilder
    .from(
        fetcher = nonFlowValueFetcher { api.fetchSubreddit(it, "10").data.children.map(::toPosts) },
        sourceOfTruth = SourceOfTrue.from(
            reader = db.postDao()::loadPosts,
            writer = db.postDao()::insertPosts,
            delete = db.postDao()::clearFeed,
            deleteAll = db.postDao()::clearAllFeeds
        )
    ).build()

然后在你的 ViewModel 中获取数据:

private val presentation: MutableLiveData<Presentation> = MutableLiveData()

lifecycleScope.launchWhenStarted {
  store.stream(StoreRequest.cached(key = key, refresh=true)).collect { response ->
    when(response) {
        // commenting some parts for brevity ...
        is StoreResponse.Data -> {
            presentation.postValue(response.value)
        }
        // ...
    }
  }
}

【讨论】:

  • 感谢您提供很好解释的答案。我会试一试;)
【解决方案2】:

我不确定这是否是最佳做法,但我认为可以解决问题。


public void onDatabaseItemsSelected() {
items.value = repository.getItemsFromdatabase().value;
};

语法可能不准确,因为我对java知之甚少,我主要做kotlin。但问题是:当用户点击 Api 时,让你的 onApiItemsSelected() 运行,这将设置 items 的值,当用户点击数据库时,让 onDatabaseItemsSelected()运行,因此它将替换 repository.getItemsFromDatabase()

结果的 items

【讨论】:

  • 这只有在你很幸运的情况下才有效,这可能永远不会(如果getItemsFromDatabase 是来自 Room DAO 的 LiveData,它肯定不会工作)。
猜你喜欢
  • 2019-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-26
  • 2020-09-12
  • 1970-01-01
相关资源
最近更新 更多