【问题标题】:Koin Dependency Injection switching between local and remote data sourceKoin Dependency Injection 在本地和远程数据源之间切换
【发布时间】:2020-10-26 23:57:34
【问题描述】:

所以我正在使用远程数据源编写这个应用程序。我想添加本地数据库存储功能。我已经建立了一个架构,我有一个接口DataSourceRemoteDataSourceLocalDataSource 类实现该接口。 RemoteDataSource 注入了 ApiInterface 改造,LocalDataSource 注入了 DAO。

现在有这个存储库接口和实现SomeDataRepositorySomeDataRepositoryImpl。如果我希望存储库能够从 api 获取数据并将其保存到数据库中,我该怎么做呢?

目前我已将RemoteDataSourceLocalDataSource 类注入SomeDataRepositoryImpl 以访问来自不同数据源的方法。这样我就可以调用类似localDataSource.saveToDb() 和/或remoteDatSource.fetchSomeData() int SomeRepositoryImpl 类的东西。但我不知道是否将具体实现传递给一个类。

但是,如果我将单个 DataSource 接口传递给 SomeDataRepository,我将必须在接口 DataSource 中定义一个 saveToDb() 函数,然后我必须在 RemoteDataSource 中实现它好吧,这不是那么好。

谁能指导我了解此解决方案的最佳方法。

而且,当我这样做的时候,在 api 接口中使用 LiveData 包装类包装数据以进行改造有什么好处吗?因为我不认为在存储库上调用方法时,我想在存储库中观察它,然后访问数据以将其放入本地数据库。

【问题讨论】:

  • 你应该使用2个接口而不是只使用一个数据源。我建议您通过 RemoteDataSourceImpl 和 LocalDataSourceImpl 创建 RemoteDataSource、LocalDataSource 接口和 impl。在 SomeDataRepositoryImpl 内部传递其 2 个实例
  • developer.android.com/jetpack/docs/guide#cache-data 在这里阅读更多内容,WebService 是您的 RemoteDataSource 而 UserCache 是 LocalDataSource
  • @CôngHải 您好,谢谢您的建议。如果我要应用一些逻辑,比如 api 请求是否成功,保存到 db 然后从 db 获取。如果失败,直接从 db 获取。如果从数据库获取失败,则只显示错误。这可以在存储库类中完成吗?或者这对于存储库的责任来说太过分了?
  • 你想通过什么逻辑来决定是使用本地还是远程数据源?
  • @MatthewPope 我只想在网络请求失败时使用本地数据源。这可能吗?

标签: android dependency-injection local-storage repository-pattern koin


【解决方案1】:

由于您想让本地数据源充当远程数据源的后备,您可以创建另一个数据源实现,它是本地和远程数据源的composition。此复合数据源可以包含回退逻辑并根据需要将delegation 处理到远程和本地数据源。完成此操作后,创建一个 Koin 模块来构建这些模块并将复合数据源绑定到数据源接口是一件简单的事情。

假设这是您的界面和您已经拥有的两个数据源:

interface DataSource {
    fun getData(): Data
}

class RemoteDataSource : DataSource {
    // ...
}

class LocalDataSource : DataSource {
    // ...
}

然后你可以像这样创建第三个实现:

class CompositeDataSource(
    val remote: RemoteDataSource, 
    val local: LocalDataSource
) : DataSource {
    override fun getData() : Data {
        return try {
            remote.getData()
        } catch (e: Exception) {
            local.getData()
        }
    }
}

要定义所有这些,您的 koin 模块看起来像这样

module {
    single { RemoteDataSource() }
    single { LocalDataSource() }
    single<DataSource> { CompositeDataSource(remote = get(), local = get()) }
}

编辑:如果你真正想要的是一个缓存,你可以像这样使用本地数据源作为你的缓存:

class CompositeDataSource(
    val remote: RemoteDataSource, 
    val local: LocalDataSource
) : DataSource {
    override fun getData() : Data {
        return try {
            remote.getData().also { local.saveData(it) }
        } catch (e: Exception) {
            local.getData()
        }
    }
}

【讨论】:

  • 您好,这是一个非常有趣且漂亮的解决方案。谢谢您的回答。我会试试这个。您能否告诉我如何在网络请求成功时管理保存数据。比如我把网络请求成功的代码保存在哪里,这样如果网络请求不成功,复合数据源就可以从本地数据库返回数据。
  • 假设您的数据源 API 有一个 write 方法,那么在您的复合数据存储实现中,您可以同时保存到远程和本地数据源。但是,如果您需要保存某些内容并且远程数据存储不可用,您还需要决定该怎么做。如果您还没有锁定到特定的后端,您可能希望考虑使用专为处理在线和离线用例而设计的 Couch DB 或 AWS AppSync。他们也许能够为您处理其中的一些复杂性,从而简化您的应用程序。
  • 我的用例是这样的,我没有将数据保存到远程源的选项。我只是想要离线存储,这样如果远程源没有给我我想要的东西,本地数据库可以提供网络请求成功时最后保存的数据。
  • 听起来你真正想要的是远程数据存储前面的缓存。我会更新我的答案来解释这一点。
  • 好的,谢谢。这正是我想要的。 :)
【解决方案2】:

您可以尝试下一个方法,它只需要很少的更改并且对我有用:

为远程和本地数据源添加接口,它应该继承主DataSource接口

interface QuotesDataSource {

fun getQuotes(skip: Int = 0, force: Boolean = false): Flow<List<Quote>>

suspend fun updateQuotes(quotes: List<Quote>)
}

interface QuotesRemoteDataSource : QuotesDataSource

interface QuotesLocalDataSource : QuotesDataSource

然后使用这些接口来创建 koin 模块

val repoModule = module {
  single { QuotesApi() }
  single<QuotesLocalDataSource> { QuotesDatabase(get()) }
  single<QuotesRemoteDataSource> { QuotesRemote(get()) }
  single<QuotesDataSource> {
      QuotesRepository(
          local = get<QuotesLocalDataSource>(),
          remote = get<QuotesRemoteDataSource>()
      )
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-08-11
    • 1970-01-01
    • 1970-01-01
    • 2020-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-21
    相关资源
    最近更新 更多