使用 Paging 3 Android 从房间获取最新数据

Posted

技术标签:

【中文标题】使用 Paging 3 Android 从房间获取最新数据【英文标题】:Get Latest Data From Room with Paging 3 Android 【发布时间】:2021-11-25 01:58:16 【问题描述】:

我想显示分页为 3 的项目列表,数据来自我的本地数据库和 Room Library。我使用房间提供的带有 JSON 文件的 RoomDatabase.Callback 将我的数据预填充到房间数据库中。但我第一次打开应用程序时,列表没有显示任何内容。我必须重新打开我的应用程序,然后表格中的项目就会显示出来。

这里是一些sn-ps的代码:

quiz.json

[
    
        "question": "Question 1",
        "type": "en",
        "answer": 0
    ,
    
        "question": "Question 2",
        "type": "en",
        "answer": 1
    
]

AppDatabase.kt

@Database(
    entities = [QuizEntity::class],
    version = 1,
    exportSchema = false
)
abstract class AppDatabase : RoomDatabase() 

    abstract fun quizDao(): QuizDao

    private class AppDatabaseCallback(
        private val context: Context,
        private val scope: CoroutineScope
    ) : RoomDatabase.Callback() 

        override fun onCreate(db: SupportSQLiteDatabase) 
            super.onCreate(db)
            INSTANCE?.let  database ->
                scope.launch 
                    val quizDao = database.quizDao()
                    quizDao.deleteAll()

                    fillQuizData(context, quizDao)
                
            
        

      suspend fun fillQuizData(context: Context, quizDao: QuizDao) 
            val jsonArray = FileUtil.loadJsonArray(context, R.raw.quiz)
            try 
                jsonArray?.let 
                    for (i in 0 until it.length()) 
                        val item = it.getJSONObject(i)
                        val isAnswer = item.getInt("answer")
                        quizDao.insert(
                            QuizEntity(
                                0,
                                item.getString("question"),
                                item.getString("type"),
                                isAnswer == 1,
                            )
                        )
                    
                
             catch (exception: JSONException) 
                exception.printStackTrace()
            
        
    

    companion object 
        @Volatile
        private var INSTANCE: AppDatabase? = null

        fun getDatabase(
            context: Context,
            scope: CoroutineScope
        ): AppDatabase 
            return INSTANCE ?: synchronized(this) 
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "MyApplication.db"
                )
                    .addCallback(AppDatabaseCallback(context, scope))
                    .fallbackToDestructiveMigration()
                    .build()
                INSTANCE = instance
                instance
            
        
    

QuizDao.kt

@Dao
interface QuizDao 

    @Query("SELECT * FROM quiz WHERE type=:type AND question LIKE '%' || :question || '%' LIMIT :size")
    suspend fun searchQuiz(type: String, size: Int, question: String = ""): List<QuizEntity>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(quizEntity: QuizEntity)

    @Query("DELETE FROM quiz")
    suspend fun deleteAll()

QuizRepository.kt

class QuizRepository @Inject constructor(private val quizDao: QuizDao) 

  fun getEnglishQuiz(query: String = ""): Flow < PagingData < QuizEntity >> 
    return Pager(PagingConfig(PAGE_SIZE)) 
      QuizPagingSource(quizDao, "en", query)
    .flow
  

  companion object 
    private
    const val PAGE_SIZE = 25
  

QuizViewModel.kt

@HiltViewModel
class QuizViewModel @Inject constructor(private val quizRepository: QuizRepository) : ViewModel() 

    fun getEnglishQuizData(query: String = ""): Flow<PagingData<Quiz>> 
        return quizRepository.getEnglishQuiz(query).map 
            it.map  quiz ->
                Quiz(
                    quiz.id,
                    quiz.question,
                    quiz.type,
                    quiz.isAnswer
                )
            
        .flowOn(Dispatchers.IO).cachedIn(viewModelScope)
    

在我的活动中:

lifecycleScope.launch 
  binding.etSearch.getQueryTextChangeStateFlow()
    .debounce(500)
    .distinctUntilChanged()
    .flatMapLatest 
      query - >
        quizViewModel.getEnglishQuizData(query)
    
    .collect 
      result - >
        quizAdapter.submitData(result)
    

SearchView 的扩展

fun SearchView.getQueryTextChangeStateFlow(): StateFlow < String > 

  val query = MutableStateFlow("")

  setOnQueryTextListener(object: SearchView.OnQueryTextListener 
    override fun onQueryTextSubmit(query: String ? ): Boolean 
      return true
    

    override fun onQueryTextChange(newText: String): Boolean 
      query.value = newText
      return true
    
  )

  return query


我曾考虑为我的 QuizDao 添加一个新函数,它返回我的 QuizEntity 的 LiveData,我在我的活动中观察它并检查大小是否不同,我将调用分页 3 库提供的函数,该库是适配器。 refreshed() 或 adapter.invalidated() 如果我没记错的话。我没有尝试过,所以我不知道这是否有效。有什么解决办法??对不起我的英语不好..谢谢。

【问题讨论】:

【参考方案1】:

通过失效通知分页对 DB 的更新,这会在Flow 中生成一个新的PagingData。在您的活动中,请确保使用.collectLatest 而不是.collect,以便您取消上一代(空)并在下游收到后使用新的一代。

例如,

lifecycleScope.launch 
  binding.etSearch.getQueryTextChangeStateFlow()
    ...
    .collectLatest 
      result - >
        quizAdapter.submitData(result)
    

.submitData 挂起,直到该代完成。由于 Paging 由收集器的作用域提供支持,因此您不应依赖它来急切地取消。

【讨论】:

以上是关于使用 Paging 3 Android 从房间获取最新数据的主要内容,如果未能解决你的问题,请参考以下文章

在 Paging 3 库 Android Kotlin 中更新当前页面或更新数据

Android Jetpack Paging 3:带 Room 的 PagingSource

Android Paging 3 - 从 Jetpack Compose 中的 PagingData<T> 对象获取数据列表

使用 Jetpack Compose 无限加载 Android Paging 3 库而无需滚动

如何在 Paging 3 中使用网络绑定资源?

从带有视图的房间 android 中获取数据