简体   繁体   中英

Android: Paging3 create remotemediator with cloud-firestore

I am trying to create a RemoteMediator with Paging3, where I cache my local results in order to save network traffic, cloud-firestore document access and to count the amount of items per query, to show placeholders.

I've already successfully implemented the "only network way" by implementing a pagingsource that access my cloud-firestore and loads the nextpage. But I am struggling with the RemoteMediator way, because there are zero tutorials how to do this in combination with cloud-firestore.

I will provide my current approach (pagingSource way) and how far I got with my RemoteMediator. I appreciate any help.

Repository

@Singleton
class ShopPagingRepositoryImpl @Inject constructor(private val db: FirebaseFirestore) : ShopPagingRepository {

    override suspend fun getCurrentPage(query: QueryHolder): QuerySnapshot = db.collection(FIREBASE_PRODUCTS_BASE_PATH)
        .limit(SHOP_DB_DOCUMENT_LIMIT)
        .whereEqualTo(FIREBASE_PRODUCT_CATEGORY, query.category)
        .orderBy(query.order)
        .get()
        .await()

    override suspend fun getNextPage(lastDocument: DocumentSnapshot, query: QueryHolder): QuerySnapshot = db.collection(FIREBASE_PRODUCTS_BASE_PATH)
        .limit(SHOP_DB_DOCUMENT_LIMIT)
        .orderBy(query.order, query.direction)
        .startAfter(lastDocument)
        .whereEqualTo(FIREBASE_PRODUCT_CATEGORY, query.category)
        .get()
        .await()
}

Current Approach (working)

class ShopPagingSource(
    private val shopRepository: ShopPagingRepository,
    private val query: QueryHolder
) : PagingSource<QuerySnapshot, Product>() {

    private companion object {
        const val SHOP_MAX_LOADING_TIME: Long = 5000L
    }

    override suspend fun load(params: LoadParams<QuerySnapshot>): LoadResult<QuerySnapshot, Product> {
        return try {
            withTimeout(SHOP_MAX_LOADING_TIME) {
                val currentPage = params.key ?: shopRepository.getCurrentPage(query)

                val nextPage: QuerySnapshot? = if (currentPage.size() != 0) {
                    val lastDocumentSnapShot = currentPage.documents[currentPage.size() - 1]
                    shopRepository.getNextPage(lastDocumentSnapShot, query)
                } else null

                LoadResult.Page(
                    data = currentPage.toObjects(),
                    prevKey = null,
                    nextKey = nextPage
                )
            }
        } catch (e: Exception) {
            Timber.e("Mediator failed, Unknown Error: ${e.message.toString()}")
            LoadResult.Error(e)
        }
    }
}

RemoteMediator approach (no clue)

@ExperimentalPagingApi
class ShopPageMediator(
    private val shopRepository: ShopPagingRepository,
    private val query: QueryHolder,
    private val shopDB: ShopDatabase
): RemoteMediator<QuerySnapshot, Product>() {

    private val shopDAO = shopDB.shopDao()
    
    override suspend fun load(
        loadType: LoadType,
        state: PagingState<QuerySnapshot, Product>,
    ): MediatorResult {
        val loadKey = when(loadType) {
            LoadType.REFRESH -> null

            LoadType.PREPEND -> return MediatorResult.Success(endOfPaginationReached = true)

            LoadType.APPEND -> {
                val lastItem = state.lastItemOrNull() ?: return MediatorResult.Success(endOfPaginationReached = true)

                // why lastitem.id, here my lastitem is product. Does this indicate the end of the page?
                lastItem.id 
            }
        }

        val currentPage = shopRepository.getCurrentPage(
            query
        )

        shopDB.withTransaction {
            if (loadType == LoadType.PREPEND) {
            //  TODO(shopDAO.deleteByQuery(query))
            }
            shopDAO.insertAll(currentPage.toObjects())
        }

        val nextPage: QuerySnapshot? = if (currentPage.size() != 0) {
            val lastDocumentSnapShot = currentPage.documents[currentPage.size() - 1]
            shopRepository.getNextPage(lastDocumentSnapShot, query)
        } else null

        // Didn't use the result of loadkey anywhere..
        return MediatorResult.Success(
            endOfPaginationReached = nextPage == null
        )
    }

}

There's many problems with your implementation, first of all, repository shouldn't be injected into data source, it should be backward and create an injected instance of the dataSource into repository with a function that provides data from dataSource with proper configuration, the correct flow involves to pull paginated data from dataSource who pulls the data from database, then when data is over, the dataSource should ask for more data to the remoteMediator, who is in charge of refresh, append or preppend data from a network source based on the type of request the dataSource is asking for, finally your repository should be injected into useCases for each function that provides de data (flow, liveData RxJava, an so...) to the UI and work with it in a recyclerView or lazyColumn(If you're using compose).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM