使用 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> 对象获取数据列表