Android RecycleView 异步缓存 itemView 提升滑动性能

RecyclerView 是 Android 官方推荐的用于展示大量数据列表的控件,具有高度的可定制性和灵活性。我们可以通过自定义 LayoutManager、ItemDecoration、ItemAnimator 等来实现不同的布局和动画效果,满足各种需求。同时,RecyclerView 支持局部刷新、数据更新等操作,能够提高列表的性能和交互体验,在我们工作当中使用得也非常频繁。

但最近在做性能检测的过程当中,设置了多布局的 RecyclerView 在快速滑动中会有一些卡顿,所以将解决方案在此文记录一下

首先需要复习一下实现 RecyclerView 的两个方法 :onCreateViewHolder 和 onBindViewHolder


Kotlin 复制代码
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
        return MyViewHolder(view)
    }

主要是 View 的渲染工作(耗时)和构建 ViewHolder 并返回


Kotlin 复制代码
    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val item = itemList[position]
        holder.bind(item)
    }

主要的工作是将已有的数据设置在 View 上进行显示处理


当 RecyclerView 滑动时,它会根据当前屏幕上显示的 item 数量和位置进行复用已有的 ViewHolder 对象,这种复用的机制可以避免频繁创建和销毁 ViewHolder 对象,提高性能。

也就是说,在理想和一般的情况下,滑动时只调用 onBindViewHolder,并不会调用 onCreateViewHolder(实际上会偶发调用)

但是如果设置了多布局的情况下 ,onCreateViewHolder 就会疯狂调用 ,因为渲染布局是在主线程中进行的,所以在这种情况下快速滑动时会 在主线程大量渲染布局 从而引发卡顿


现在有两个问题:① onCreateViewHolder 频繁得调用 ② 布局的渲染在主线程

解决方案:① 缓存 ② 线程池

Kotlin 复制代码
class MyAdapter : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {

    private val cacheViewSize = 10 // 可根据实际需求调整缓存大小
    private val executors = Executors.newSingleThreadExecutor()
    private val cacheMap = mutableMapOf<Int, LinkedList<View>>()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val layoutResId = R.layout.xxx // 不同的 viewType 有不同的布局自行处理即可

        val cacheViewOrGenerateView = getView(parent, layoutResId)
        return MyViewHolder(cacheViewOrGenerateView)
    }

    private fun getView(parent: ViewGroup, layoutRes: Int): View {
        if (cacheMap[layoutRes] == null) {
            cacheMap[layoutRes] = LinkedList()
        }
        cacheMap[layoutRes]?.let { list ->
            if (list.isEmpty()) {
                viewCache(parent, layoutRes)
            } else {
                val view = list.poll()
                if (view != null) {
                    viewCache(parent, layoutRes) // 拿一个补一个
                    return view
                }
            }
        }
        return LayoutInflater.from(parent.context).inflate(layoutRes, parent, false) // 兜底处理
    }

    /**
     * 渲染布局至缓存,直到 cacheViewSize 为止
     */
    private fun viewCache(parent: ViewGroup, layoutRes: Int) {
        executors.execute {
            cacheMap[layoutRes]?.let { list ->
                while (list.size < cacheViewSize) {
                    list.add(parent.getItemView(layoutRes))
                }
            }
        }
    }
    
    // ....
    
    open class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {}
}

将布局的渲染放线程池里执行,并设置固定缓存,onCreateViewHolder 中的 View 优先从缓存中获取,若没有缓存使用同步渲染作兜底处理

相关推荐
TeleostNaCl15 小时前
如何安装 Google 通用的驱动以便使用 ADB 和 Fastboot 调试(Bootloader)设备
android·经验分享·adb·android studio·android-studio·android runtime
fatiaozhang952716 小时前
中国移动浪潮云电脑CD1000-系统全分区备份包-可瑞芯微工具刷机-可救砖
android·网络·电脑·电视盒子·刷机固件·机顶盒刷机
2501_9159184117 小时前
iOS 开发全流程实战 基于 uni-app 的 iOS 应用开发、打包、测试与上架流程详解
android·ios·小程序·https·uni-app·iphone·webview
lichong95117 小时前
【混合开发】vue+Android、iPhone、鸿蒙、win、macOS、Linux之dist打包发布在Android工程asserts里
android·vue.js·iphone
Android出海17 小时前
Android 15重磅升级:16KB内存页机制详解与适配指南
android·人工智能·新媒体运营·产品运营·内容运营
一只修仙的猿17 小时前
毕业三年后,我离职了
android·面试
编程乐学18 小时前
安卓非原创--基于Android Studio 实现的新闻App
android·ide·android studio·移动端开发·安卓大作业·新闻app
雅雅姐19 小时前
Android14 init.rc中on boot阶段操作4
android
fatiaozhang952719 小时前
中国移动中兴云电脑W132D-RK3528-2+32G-刷机固件包(非原机制作)
android·xml·电脑·电视盒子·刷机固件·机顶盒刷机
Android出海21 小时前
Google Play账户与App突遭封禁?紧急应对与快速重构上架策略
android·网络·重构·新媒体运营·产品运营·内容运营