我遇到了一些问题,因为当对底层数据库表进行更改时,Room数据流不会发出更新.

API Service

interface AtemsService {

    @GET("$PATH/data/_apps.json")
    suspend fun getPackageData(): Map<String, AtemsPackageInfo>
}

Remote Impl

class AtemsRemoteImpl @Inject constructor(private val atemsService: AtemsService) : AtemsRemote {

    override suspend fun getAtemsPackageData(): Map<String, AtemsPackageInfo> =
        atemsService.getPackageData()
}

Local Dao

@Dao
interface AtemsPackageDao {

    @Query("SELECT * FROM $TABLE_NAME")
    suspend fun getAll(): List<LocalAtemsPackage>

    @Upsert
    suspend fun upsertAll(localAtemsPackages: List<LocalAtemsPackage>)
}

Repo Impl

class AtemsPackageRepositoryImpl @Inject constructor(
    private val atemsRemote: AtemsRemote,
    private val atemsPackageDao: AtemsPackageDao,
    private val atemsPackageMapper: AtemsPackageMapper,
) : AtemsPackageRepository {

    override fun observeAtemsPackages(): Flow<List<AtemsPackage>> {
        return flow {
            val localAtemsPackages = atemsPackageDao.getAll()
            emit(atemsPackageMapper.localToDomain(localAtemsPackages))
            if (localAtemsPackages.isEmpty()) {
                val fetchRemotePackages = atemsRemote.getAtemsPackageData()
                val convertToLocal = atemsPackageMapper.remoteToLocal(fetchRemotePackages)
                atemsPackageDao.upsertAll(convertToLocal)
            }
        }
    }
}

Use Case

class AppsUseCaseImpl @Inject constructor(
    atemsPackageRepository: AtemsPackageRepository,
) : AppsUseCase {

    override val observeApps: Flow<List<AtemsPackage>> = atemsPackageRepository.observeAtemsPackages()
}

View Model

@HiltViewModel
class SidePanelAppListViewModel @Inject constructor(appsUseCase: AppsUseCase) : ViewModel() {

    val apps = appsUseCase.observeApps.stateIn(
        scope = viewModelScope,
        started = WhileUiSubscribed,
        initialValue = emptyList()
    )
}

最后, View

@Composable
fun SidePanelAppList(
    modifier: Modifier = Modifier,
    viewModel: SidePanelAppListViewModel = hiltViewModel(),
) {
    val apps by viewModel.apps.collectAsStateWithLifecycle()

    SidePanelAppList(
        modifier = Modifier.then(modifier),
        apps = apps
    )
}

除了repo中的方法进行网络调用以检索数据之外,一切都按预期进行,尽管成功地将数据保存到Room数据库,但除非应用程序重新启动,否则我的用户界面层不会收集/显示更新.任何指导都将不胜感激!

Update

遵循Tenour04非常有用的正确方向,我完成了这个(需求略有变化,因为我现在根据从遥控器检索到的值进行更新.

@OptIn(ExperimentalCoroutinesApi::class)
override fun observeAtemsPackages(): Flow<List<AtemsPackage>> =
    atemsPackageDao.observeAll().transformLatest {
        val lastLocalUpdate = settingsDataStore.getAtemsRemoteLastUpdate.first()
        val lastRemoteUpdate = atemsRepoRemote.getAtemsLastUpdate().lastUpdated
        if (lastRemoteUpdate > lastLocalUpdate) {
            val fetchRemotePackages = atemsRepoRemote.getAtemsPackageData()
            val convertToLocal = atemsPackageMapper.remoteToLocal(fetchRemotePackages)
            atemsPackageDao.upsertAll(convertToLocal)
            settingsDataStore.setAtemsRemoteLastUpdate(lastRemoteUpdate)
        }
    emit(atemsPackageMapper.localToDomain(it))
    }

推荐答案

在更新数据库后,在Obview方法中创建的流不会发出任何内容.我认为最干净的做法是让DAO将列表公开为流,并在需要时使用onEach来触发获取.

DAO:

@Dao
interface AtemsPackageDao {

    @Query("SELECT * FROM $TABLE_NAME")
    fun getAll(): Flow<List<LocalAtemsPackage>>

    @Upsert
    suspend fun upsertAll(localAtemsPackages: List<LocalAtemsPackage>)
}

在回购实施中:

override fun observeAtemsPackages(): Flow<List<AtemsPackage>> 
    = atemsPackageDao.getAll()
        .onEach {
            if (it.isEmpty()) {
                val fetchRemotePackages = atemsRemote.getAtemsPackageData()
                val convertToLocal = atemsPackageMapper.remoteToLocal(fetchRemotePackages)
                atemsPackageDao.upsertAll(convertToLocal)
            }
        }

现在,您的流完全返回最新的数据库状态.onEach的行动是一种副作用.

如果可以获取空列表,则可能需要执行一些操作,以便它只try 获取一次:

override fun observeAtemsPackages(): Flow<List<AtemsPackage>> 
    = flow {
        val source = atemsPackageDao.getAll()
        if (source.first().isEmpty()) {
            val fetchRemotePackages = atemsRemote.getAtemsPackageData()
            val convertToLocal = atemsPackageMapper.remoteToLocal(fetchRemotePackages)
            atemsPackageDao.upsertAll(convertToLocal)
        }
        emitAll(source)
    }

Kotlin相关问答推荐

这些Kotlin函数等效吗?

有什么原因,我得到一个空相关的错误在这个代码

文本正在被切断在200%的屏幕比例在Jetpack Compose

用普通Kotlin理解Gradle的Kotlin DSL'""

映射中列表类型的Kotlin可空接收器?

可以从背景图像中点击图标吗?

S使用MAP和ElseThrow的习惯用法是什么?

禁用 Gradle执行消息

Spring Boot Kotlin 数据类未使用 REST 控制器中的默认值初始化

在 kotlin 原始字符串中转义三重引号

将 java Optional 转换为 Kotlin Arrow Option

kotlin 父类具有依赖于抽象变量的变量

参考 Kotlin 中的 Java 接口静态字段

kotlin,如何从函数返回类类型

如何在 Kotlin 中使用 volatile

是否可以在 kotlin 中嵌套数据类?

Kotlin 的 isNullOrBlank() 函数是否可以导入 xml 以用于数据绑定

如何在 Android Studio 3.1.3 中查看 Kotlin 中有趣的源代码?

Kotlin - 是否可以在类中的 init 块之前初始化伴随对象?

访问Kotlin中的属性委托