You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通过UseCase观察Room数据库Flow时数据更新不发射的问题排查

核心问题:通过UseCase获取Room Flow时,数据更新不触发Flow发射

在采用Clean Architecture、MVVM架构的Android笔记应用中,使用Dagger Hilt实现依赖注入,结合Room数据库与Flow做数据监听。遇到如下问题:

  • 直接调用Repository暴露的Flow时,向数据库添加笔记后,Flow会正常发射包含新笔记的列表;
  • 通过UseCase封装相同逻辑后,笔记虽成功存入数据库,但获取笔记的Flow不会发射数据更新。

相关代码实现

NotesDao

@Dao
interface NotesDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun addNote(noteEntity: NoteEntity)

    @Update
    suspend fun update(noteEntity: NoteEntity)

    @Delete
    suspend fun delete(noteEntity: NoteEntity)

    @Query("SELECT * FROM notes WHERE (:query IS NULL OR :query = '' OR LOWER(title) LIKE '%' || LOWER(:query) || '%' OR LOWER(content) LIKE '%' || LOWER(:query) || '%')")
    fun getNotesFlow(query: String? = null): Flow<List<NoteEntity>>
}

LocalNotesDataSourceImpl

class LocalNotesDataSourceImpl(
    private val notesDao: NotesDao
) : LocalNotesDataSource {

    override suspend fun addNote(note: Note) {
        notesDao.addNote(note.toNoteEntity())
    }

    override suspend fun updateNote(note: Note) {
        notesDao.update(note.toNoteEntity())
    }

    override suspend fun deleteNote(note: Note) {
        notesDao.delete(note.toNoteEntity())
    }

    override fun getNotesFlow(query: String?): Flow<List<Note>> {
        return notesDao.getNotesFlow(query)
            .map { notes ->
                notes.map { it.toNote() }
            }
    }
}

NotesRepositoryImpl

class NotesRepositoryImpl(
    private val localNotesDataSource: LocalNotesDataSource
) : NotesRepository {

    override suspend fun addNote(note: Note) {
        localNotesDataSource.addNote(note)
    }

    override suspend fun deleteNote(note: Note) {
        localNotesDataSource.deleteNote(note)
    }

    override fun getNotesFlow(query: String?): Flow<List<Note>> {
        return localNotesDataSource.getNotesFlow(query)
    }
}

UseCase实现

class GetNotesFlowUseCase(
    private val notesRepository: NotesRepository
) {
    fun invoke(query: String? = null) = notesRepository.getNotesFlow(query)
}

class AddNoteUseCase(
    private val notesRepository: NotesRepository
) {
    suspend fun invoke(note: Note) = notesRepository.addNote(note)
}

UseCase依赖注入配置

@Module
@InstallIn(SingletonComponent::class)
object UseCaseModule {

    @Provides
    fun provideGetNotesFlowUseCase(notesRepository: NotesRepository): GetNotesFlowUseCase =
        GetNotesFlowUseCase(notesRepository)

    @Provides
    fun provideAddNoteFlowUseCase(notesRepository: NotesRepository): AddNoteUseCase =
        AddNoteUseCase(notesRepository)
}

UI层代码

NotesListViewModel
@HiltViewModel
class NotesListViewModel @Inject constructor(
    private val getNotesFlowUseCase: GetNotesFlowUseCase,
    private val addNoteUseCase: AddNoteUseCase,
) : ViewModel() {

    private val _uiState = MutableStateFlow(NoteListUiState())
    val uiState = _uiState.asStateFlow()

    init {
        observeNotesFlow()
    }

    private fun observeNotesFlow() {
        viewModelScope.launch {
            // 此处直接调用notesRepository.getNotesFlow(query)可正常工作
            getNotesFlowUseCase.invoke().collect { notes ->
                _uiState.value =
                    _uiState.value.copy(
                        notes = notes.map { it.toNoteItemUi() },
                        isLoading = false
                    )
            }
        }
    }

    fun addNote() {
        viewModelScope.launch {
            // 此处直接调用notesRepository.addNote(note)可正常工作
            addNoteUseCase.invoke(
                Note(
                    id = UUID.randomUUID(),
                    title = "Title",
                    content = "Content",
                    timestamp = System.currentTimeMillis(),
                    isInTrash = false,
                    isArchived = false
                )
            )
        }
    }
}
NotesListFragment
@AndroidEntryPoint
class NotesListFragment : BaseFragment<FragmentNotesListBinding>() {

    override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentNotesListBinding =
        FragmentNotesListBinding::inflate

    private val viewModel: NotesListViewModel by viewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewModel.uiState
            .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
            .onEach { uiState ->
                Timber.tag("NOTES FLOW").d(uiState.notes.toString())
            }
            .launchIn(lifecycleScope)

        binding.fabAddNote.setOnClickListener {
            viewModel.addNote()
        }
    }
}

目前的核心疑问:通过UseCase调用添加笔记后,笔记已成功存入数据库,但获取笔记的Flow未发射更新;直接调用Repository则一切正常。请问我的UseCase实现哪里出现了问题?

内容的提问来源于stack exchange,提问作者Tomasz Testowy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 00:40:58