如何结合我自己的DataDource和BoundaryCallback?

问题描述 投票:0回答:1

我基于数据库+网络分页和GitHub Rest api创建应用程序。 使用各种教程,我得出的结论是,在 ViewModel 中创建 LivePagedListBuilder 时,我必须传递从 Room 检索数据的查询,以使其能够与 BoundaryCallback 一起使用。 我的代码中的查询如下所示:

@Query("SELECT * from repositories_table ORDER BY name DESC")
    fun getPagedRepos(): DataSource.Factory<Int,Repository>

及其在存储库中的等效项:

fun getPagedRepos(): DataSource.Factory<Int, Repository> {
    return repositoriesDao.getPagedRepos()
}

但是我想将其与我的自己的数据源(而不是默认数据源)结合起来,这也可以用于改造数据获取。 以下是我的申请的相关部分:

数据源

class ReposDataSource(private val contactsRepository: ContactsRepository,
                        private val scope: CoroutineScope, application: Application): PageKeyedDataSource<Int, Repository>() {

private var supervisorJob = SupervisorJob()

private val PREFS_NAME = "Paging"

private val sharedPref: SharedPreferences = application.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)

override fun loadInitial(
    params: LoadInitialParams<Int>,
    callback: LoadInitialCallback<Int, Repository>
) {
    Log.i("RepoBoundaryCallback", "initialTriggered")
    val currentPage = 1
    val nextPage = currentPage + 1

    executeQuery(currentPage, params.requestedLoadSize) {
        callback.onResult(it, null, nextPage)
    }
}

override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, Repository>) {

    val currentPage = params.key
    val nextPage = currentPage + 1

    executeQuery(currentPage, params.requestedLoadSize) {
        callback.onResult(it, nextPage)
    }
}

override fun invalidate() {
    super.invalidate()
    supervisorJob.cancelChildren()
}

private fun executeQuery(page: Int, perPage: Int, callback: (List<Repository>) -> Unit) {
    scope.launch(getJobErrorHandler() + supervisorJob) {
        savePage("current_page", page)
        val repos = contactsRepository.fetchPagedRepos(page, perPage)
        callback(repos)
    }
}

private fun getJobErrorHandler() = CoroutineExceptionHandler { _, e ->
    Log.e(ReposDataSource::class.java.simpleName, "An error happened: $e")
}

private fun savePage(KEY_NAME: String, value: Int){
    Log.i("RepoBoundaryCallback", value.toString())
    val editor: SharedPreferences.Editor = sharedPref.edit()
    editor.putInt(KEY_NAME, value)
    editor.commit()
}

}

边界回调

class RepoBoundaryCallback (val repository: ContactsRepository, application: Application) :
PagedList.BoundaryCallback<Repository?>() {


private var callbackJob = Job()

private val coroutineScope = CoroutineScope(
    callbackJob + Dispatchers.Main )

private val PREFS_NAME = "Paging"

private val sharedPref: SharedPreferences = application.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)

override fun onZeroItemsLoaded() {
    Log.i("RepoBoundaryCallback", "onzeroitemstriggered")
    super.onZeroItemsLoaded()
    fetchUsers(1)
}

override fun onItemAtEndLoaded(itemAtEnd: Repository) {
    Log.i("RepoBoundaryCallback", "onitematendriggered")
    super.onItemAtEndLoaded(itemAtEnd)
    fetchUsers(getCurrentPage("current_page"))
}

private fun fetchUsers(page: Int) {
    coroutineScope.launch {
        try {
            var newRepos = RepoApi.retrofitService.fetchRepos(page)
            insertRepoToDb(newRepos)
        }
        catch (e: Exception){
            Log.i("RepoBoundaryCallback", e.toString())
        }
    }
}

 private suspend fun insertRepoToDb(reposList: List<Repository>){
    reposList.forEach{repository.insertRepo(it)}
}

private fun getCurrentPage(KEY_NAME: String): Int{
    return sharedPref.getInt(KEY_NAME, 0)
}

}

API查询

interface RepoApiService {
@GET("/orgs/google/repos")
suspend fun fetchRepos(@Query("page") page: Int,
                       @Query("per_page") perPage: Int = 15): List<Repository>

}

视图模型

class RepositoryViewModel (application: Application) : AndroidViewModel(application) {

companion object{
    private const val TAG = "RepositoryViewModel"
}

//var reposList: LiveData<PagedList<Repository>>

private var repoBoundaryCallback: RepoBoundaryCallback? = null

var reposList: LiveData<PagedList<Repository>>? = null

private val repository: ContactsRepository

private var viewModelJob = Job()

private val coroutineScope = CoroutineScope(
    viewModelJob + Dispatchers.Main )

init {
    val contactsDao = ContactsRoomDatabase.getDatabase(application, viewModelScope).contactsDao()
    val contactsExtrasDao = ContactsRoomDatabase.getDatabase(application, viewModelScope).contactsExtrasDao()
    val repositoriesDao = ContactsRoomDatabase.getDatabase(application, viewModelScope).repositoriesDao()
    val service = RepoApi.retrofitService
    repository = ContactsRepository(contactsDao, contactsExtrasDao, repositoriesDao, service)
    initializedPagedListBuilder(application)
}


private fun initializedPagedListBuilder(application: Application) {
    repoBoundaryCallback = RepoBoundaryCallback(
        repository, application
    )

    val pagedListConfig = PagedList.Config.Builder()
        //.setPrefetchDistance(5)
        //.setInitialLoadSizeHint(20)
        .setEnablePlaceholders(true)
        .setPageSize(15).build()

    reposList = LivePagedListBuilder(
        repository.getPagedRepos(),
        pagedListConfig
    ).setBoundaryCallback(repoBoundaryCallback).build()
}


override fun onCleared() {
    super.onCleared()
    viewModelJob.cancel()
}
}

此外,我将相关页面保存在DataSource中的SharedPreferences中,然后在相应的BoundaryCallback函数中使用它。 那么如何使用 Room 和 Retrofit 将自己的 DataSource 链接到 BoundaryCallback 呢?我将不胜感激任何帮助。

android kotlin pagination retrofit android-room
1个回答
0
投票

BoundaryCallback
负责触发当前一代
DataSource
的失效。对于由 Room 生成的
DataSource.Factory
,这会自动为您处理,因为 Room 将使它生成的任何受数据库写入影响的
DataSource
无效。这就是为什么需要
DataSource.Factory
而不是单个
DataSource
。分页将
DataSource
的单个实例视为静态数据的“快照”。如果要加载的数据以任何方式发生更改,您必须调用
DataSource.invalidate()
以允许
DataSource.Factory
生成新的最新快照。

由于您正在实现自己的

DataSource
,因此您还需要实现
DataSource.Factory
并从您的
invalidate()
调用
BoundaryCallback
(不一定需要在同一个类中,但
invalidate()
必须在您的
BoundaryCallback
写入更新时触发)。

© www.soinside.com 2019 - 2024. All rights reserved.