Paging 3 库使用 LoadType.APPEND 递归调用加载方法

2023-12-25

我正在尝试显示来自图标查找器 API https://developer.iconfinder.com/reference/getting-started-with-your-api-1。好像是ItemKeyedDataSource对我来说,我用过Paging3显示数据中提到的官方文档 https://developer.android.com/topic/libraries/architecture/paging/v3-network-db#create-pager.

这是代码,请检查我所做的实现是否有问题以及错误在哪里。

图标集RemoteMediator

@OptIn(ExperimentalPagingApi::class)
class IconSetsRemoteMediator(
    private val query: String?,
    private val database: IconsFinderDatabase,
    private val networkService: IconFinderAPIService
) : RemoteMediator<Int, IconSetsEntry>() {

    private val TAG: String? = IconSetsRemoteMediator::class.simpleName
    private val iconSetsDao = database.iconSetsDao
    private val remoteKeysDao = database.remoteKeysDao

    override suspend fun initialize(): InitializeAction {
        // Load fresh data when ever the app is open new
        return InitializeAction.LAUNCH_INITIAL_REFRESH
    }

    override suspend fun load(
        loadType: LoadType,
        state: PagingState<Int, IconSetsEntry>
    ): MediatorResult {

        val iconSetID = when (loadType) {
            LoadType.REFRESH -> {
                
                null
            }
            LoadType.PREPEND -> {

                return MediatorResult.Success(
                    endOfPaginationReached = true
                )
            }
            LoadType.APPEND -> {
                Log.d(TAG, "LoadType.APPEND")

                val lastItem = state.lastItemOrNull()

                if (lastItem == null) {
                    return MediatorResult.Success(
                        endOfPaginationReached = true
                    )
                }
                // Get the last item from the icon-sets list and return its ID
                lastItem.iconset_id
            }
        }

        try {
            // Suspending network load via Retrofit.
            val response = networkService.getAllPublicIconSets(after = iconSetID)
            val iconSets = response.iconsets
            val endOfPaginationReached = iconSets == null || iconSets.isEmpty()


            database.withTransaction {
                if (loadType == LoadType.REFRESH) {
                    // Delete the data in the database
                    iconSetsDao.deleteAllIconSets()
                    //remoteKeysDao.deleteRemoteKeys()
                }

                Log.d(TAG, "iconSets = ${iconSets?.size}")
                Log.d(TAG, "endOfPaginationReached = $endOfPaginationReached")
                Log.d(TAG, "state.anchorPosition = ${state.anchorPosition}")
                Log.d(TAG, "state.pages = ${state.pages.size}")

                val time = System.currentTimeMillis()
                /*val remoteKeys = iconSets!!.map {
                    RemoteKeysEntry(it.iconset_id, time)
                }*/

                // Insert new IconSets data into database, which invalidates the current PagingData,
                // allowing Paging to present the updates in the DB.
                val data = iconSets!!.mapAsIconSetsEntry()
                iconSetsDao.insertAllIconSets(data)

                // Insert the remote key values which set the time at which the data is
                // getting updated in the DB
                //remoteKeysDao.insertRemoteKeys(remoteKeys)
            }
            return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
        } catch (exception: IOException) {
            return MediatorResult.Error(exception)
        } catch (exception: HttpException) {
            return MediatorResult.Error(exception)
        }
    }

}

IconFinder存储库

class IconFinderRepository(
    private val service: IconFinderAPIService,
    private val database: IconsFinderDatabase
) {
    private val TAG: String? = IconFinderRepository::class.simpleName

    fun getPublicIconSets(): Flow<PagingData<IconSetsEntry>> {
        Log.d(TAG, "New Icon Sets query")

        val pagingSourceFactory = { database.iconSetsDao.getIconSets() }

        @OptIn(ExperimentalPagingApi::class)
        return Pager(
            config = PagingConfig(pageSize = NUMBER_OF_ITEMS_TO_FETCH, enablePlaceholders = false),
            remoteMediator = IconSetsRemoteMediator(
                query = null,
                database,
                service
            ),
            pagingSourceFactory = pagingSourceFactory
        ).flow
    }

    companion object {
        const val NUMBER_OF_ITEMS_TO_FETCH = 20
    }
}

图标集ViewHolder

class IconSetViewHolder private constructor(val binding: RecyclerItemIconSetBinding) :
    RecyclerView.ViewHolder(binding.root) {

    fun bind(iconSetsEntry: IconSetsEntry?) {
        if (iconSetsEntry == null) {
            //Show the Loading UI
        } else {
            binding.model = iconSetsEntry
            binding.executePendingBindings()
        }
    }

    companion object {
        fun from(parent: ViewGroup): IconSetViewHolder {
            val layoutInflater = LayoutInflater.from(parent.context)
            val binding = RecyclerItemIconSetBinding.inflate(layoutInflater, parent, false)
            return IconSetViewHolder(binding)
        }
    }
}

图标集适配器

class IconSetAdapter : PagingDataAdapter<UiModel.IconSetDataItem, ViewHolder>(UI_MODEL_COMPARATOR) {

    companion object {
        private val UI_MODEL_COMPARATOR =
            object : DiffUtil.ItemCallback<UiModel.IconSetDataItem>() {
                override fun areContentsTheSame(
                    oldItem: UiModel.IconSetDataItem,
                    newItem: UiModel.IconSetDataItem
                ): Boolean {
                    return oldItem.iconSetsEntry.iconset_id == newItem.iconSetsEntry.iconset_id
                }

                override fun areItemsTheSame(
                    oldItem: UiModel.IconSetDataItem,
                    newItem: UiModel.IconSetDataItem
                ): Boolean =
                    oldItem == newItem
            }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return if (viewType == R.layout.recycler_item_icon_set) {
            IconSetViewHolder.from(parent)
        } else {
            IconSetViewHolder.from(parent)
        }
    }

    override fun getItemViewType(position: Int): Int {
        return when (getItem(position)) {
            is UiModel.IconSetDataItem -> R.layout.recycler_item_icon_set
            null -> throw UnsupportedOperationException("Unknown view")
            else -> throw UnsupportedOperationException("Unknown view")
        }
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val uiModel = getItem(position)
        uiModel.let {
            when (uiModel) {
                is UiModel.IconSetDataItem -> (holder as IconSetViewHolder).bind(uiModel.iconSetsEntry)
            }
        }
    }
}

HomeFragmentViewModel

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

    private val TAG: String? = HomeFragmentViewModel::class.simpleName
 
    private val repository: IconFinderRepository = IconFinderRepository(
        IconFinderAPIService.create(),
        IconsFinderDatabase.getInstance(application)
    )

    private var iconSetsQueryResult: Flow<PagingData<UiModel.IconSetDataItem>>? = null

    fun iconSetsQuery(): Flow<PagingData<UiModel.IconSetDataItem>> {

        val newResult: Flow<PagingData<UiModel.IconSetDataItem>> = repository.getPublicIconSets()
            .map { pagingData -> pagingData.map { UiModel.IconSetDataItem(it) } }
            .cachedIn(viewModelScope)

        iconSetsQueryResult = newResult
        return newResult

    }

    /**
     * Factory for constructing HomeFragmentViewModel
     */
    class Factory(private val application: Application) : ViewModelProvider.Factory {
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            if (modelClass.isAssignableFrom(HomeFragmentViewModel::class.java)) {
                return HomeFragmentViewModel(application) as T
            }
            throw  IllegalArgumentException("Unable to construct ViewModel")
        }
    }
}

sealed class UiModel {
    data class IconSetDataItem(val iconSetsEntry: IconSetsEntry) : UiModel()
}

图标集片段:这是作为一部分实现的片段之一ViewPager。它的父级是一个Fragment in an Activity.

class IconSetFragment : Fragment() {

    private val TAG: String = IconSetFragment::class.java.simpleName

    /**
     * Declaring the UI Components
     */
    private lateinit var binding: FragmentIconSetBinding

    private val viewModel: HomeFragmentViewModel by viewModels()
    private val adapter = IconSetAdapter()
    private var job: Job? = null

    companion object {
        fun newInstance(): IconSetFragment {
            return IconSetFragment()
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        // Get a reference to the binding object
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_icon_set, container, false)
        Log.d(TAG, "onCreateView")
        return binding.root
    }

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

        job?.cancel()
        job = viewLifecycleOwner.lifecycleScope.launch {
            viewModel.iconSetsQuery().collectLatest {
                adapter.submitData(it)
                Log.d(TAG, "collectLatest $it")
            }
        }

    }

    private fun initAdapter() {
        binding.rvIconSetList.adapter = adapter
        /*.withLoadStateHeaderAndFooter(
        header = LoadStateAdapter(), // { adapter.retry() },
        footer = LoadStateAdapter { adapter.retry() }
    )*/
    }
}

图标集Dao

@Dao
interface IconSetsDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertAllIconSets(iconSets: List<IconSetsEntry>)

    @Query("SELECT * FROM icon_sets_table")
    fun getIconSets(): PagingSource<Int, IconSetsEntry>

    @Query("DELETE FROM icon_sets_table")
    suspend fun deleteAllIconSets()
}

This is the Logcat screenshot, the load() method is being invoked without any scrolling action. enter image description here


我有类似的问题,似乎递归加载问题通过设置来解决recyclerView.setHasFixedSize(true)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Paging 3 库使用 LoadType.APPEND 递归调用加载方法 的相关文章

  • 如何快速自动发送FCM或APNS消息?

    我正在开发一项后端服务 通过 FCM 或 APNS 向移动应用程序发送推送通知 我想创建一个可以在一分钟内运行的自动化测试 并验证服务器是否可以成功发送通知 请注意 我不一定需要检查通知是否已送达 只需检查 FCM 或 APNS 是否已成功
  • Android Studio 3.0 Canary 9 - 无法解析包

    我在 Android Studio 3 0 Canary 9 中遇到几个错误 这些错误是 无法解析 android 软件包 下面列出了一些错误 我刚刚安装了 SDK 的所有额外软件包 但仍然收到 gradle 构建错误 Error 82 1
  • 类型容器“Android 依赖项”引用不存在的库 android-support-v7-appcompat/bin/android-support-v7-appcompat.jar

    我在尝试在我的项目中使用 Action Bar Compat 支持库时遇到了某种错误 我不知道出了什么问题 因为我已按照此链接中的说明进行操作 gt http developer android com tools support libr
  • 卸载后 Web 应用程序不显示“添加到主屏幕”

    这是我第一次创建网络应用程序 我设法解决了这个问题 所以我得到了实际的 chrome 提示 将其添加到主屏幕 然后我从手机上卸载了该网络应用程序 因为我想将其展示给我的同事 但是 屏幕上不再出现提示 问题 这是有意为之的行为还是我的应用程序
  • Android 30+ 中的视频捕获意图 - 只有所有者才能与待处理项目交互

    我正在尝试在我的应用程序上捕获视频 它可以在 android API 30 以下运行 但不能在 30 以上运行 似乎在 sdk 30 之后 android 不允许完全读取外部存储 作用域存储 我目前遇到这个错误 java lang Ille
  • 找不到 com.google.firebase:firebase-core:9.0.0 [重复]

    这个问题在这里已经有答案了 在遵循有些不一致的指示之后here https firebase google com docs admob android quick start name your project and here http
  • CardView 圆角获得意想不到的白色

    When using rounded corner in CardView shows a white border in rounded area which is mostly visible in dark environment F
  • Adobe 是否为其 PDF 阅读器提供 Android SDK 或 API? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我希望能够在我们的应用程序内的视图中显示本地 PDF 文件 在 Android 4 03 下的平板电脑上运行 目前 我们将 Adob eR
  • Android SIP 来电使用带有广播接收器的服务

    大家好 其实我正在尝试创建一个应用程序 支持基于 SIP 通过互联网进行音频呼叫 这里使用本机 sip 我遇到了来电问题 我已经完成了服务的注册部分 但是在接听电话时我无法接听电话 请帮助我 Service file package exa
  • 使用 Android 发送 HTTP Post 请求

    我一直在尝试从 SO 和其他网站上的大量示例中学习 但我无法弄清楚为什么我编写的示例不起作用 我正在构建一个小型概念验证应用程序 它可以识别语音并将其 文本 作为 POST 请求发送到 node js 服务器 我已确认语音识别有效 并且服务
  • 无法访问 com.google.android.gms.internal.zzbfm 的 zzbfm 类文件未找到

    我正在将我的 Android 应用程序项目从GCM to FCM 为此 我使用 Android Studio 中的 Firebase 助手工具 并遵循 Google 开发人员指南中的说明 一切都很顺利 并将我的应用程序代码更改为FCM根据助
  • 尝试将相机切换回前面但出现异常

    尝试将相机切换回前面 但出现异常 找不到 问题请检查并帮助 error 01 27 11 49 00 376 E AndroidRuntime 30767 java lang RuntimeException Unable to start
  • 在gradle插件中获取应用程序变体的包名称

    我正在构建一个 gradle 插件 为每个应用程序变体添加一个新任务 此新任务需要应用程序变体的包名称 这是我当前的代码 它停止使用最新版本的 android gradle 插件 private String getPackageName
  • JavaMail 只获取新邮件

    我想知道是否有一种方法可以在javamail中只获取新消息 例如 在初始加载时 获取收件箱中的所有消息并存储它们 然后 每当应用程序再次加载时 仅获取新消息 而不是再次重新加载它们 javamail 可以做到这一点吗 它是如何工作的 一些背
  • 在两个活动之间传输数据[重复]

    这个问题在这里已经有答案了 我正在尝试在两个不同的活动之间发送和接收数据 我在这个网站上看到了一些其他问题 但没有任何问题涉及保留头等舱的状态 例如 如果我想从 A 类发送一个整数 X 到 B 类 然后对整数 X 进行一些操作 然后将其发送
  • Android Studio 0.4.3 Eclipse项目没有gradle

    在此版本之前 在 Android Studio 中按原样打开 Eclipse 项目似乎很容易 无需任何转换 我更喜欢 Android Studio 环境 但我正在开发一个使用 eclipse 作为主要 IDE 的项目 我不想只为这个项目下载
  • Android访问远程SQL数据库

    我可以直接从 Android 程序访问远程 SQL 数据库 在网络服务器上 吗 即简单地打开包含所有必需参数的连接 然后执行 SQL 查询 这是一个私人程序 不对公众开放 仅在指定的手机上可用 因此我不担心第三方获得数据库访问权限 如果是这
  • Firebase 添加新节点

    如何将这些节点放入用户节点中 并创建另一个节点来存储帖子 我的数据库参考 databaseReference child user getUid setValue userInformations 您需要使用以下代码 databaseRef
  • 将两个文本视图并排放置在布局中

    我有两个文本视图 需要在布局中并排放置 并且必须遵守两条规则 Textview2 始终需要完整显示 如果布局中没有足够的空间 则必须裁剪 Textview1 例子 文本视图1 文本视图2 Teeeeeeeeeeeeeeeeeextview1
  • android sdk 的位置尚未在 Windows 操作系统的首选项中设置

    在 Eclipse 上 我转到 windows gt Android SDK 和 AVD Manager 然后弹出此消息 Android sdk 的位置尚未在首选项中设置 进入首选项 在侧边栏找到 Android 然后会出现一个 SDK 位

随机推荐