当前位置:   article > 正文

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

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

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

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

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


  1. override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
  2. val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
  3. return MyViewHolder(view)
  4. }

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


  1. override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
  2. val item = itemList[position]
  3. holder.bind(item)
  4. }

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


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

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

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


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

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

  1. class MyAdapter : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
  2. private val cacheViewSize = 10 // 可根据实际需求调整缓存大小
  3. private val executors = Executors.newSingleThreadExecutor()
  4. private val cacheMap = mutableMapOf<Int, LinkedList<View>>()
  5. override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
  6. val layoutResId = R.layout.xxx // 不同的 viewType 有不同的布局自行处理即可
  7. val cacheViewOrGenerateView = getView(parent, layoutResId)
  8. return MyViewHolder(cacheViewOrGenerateView)
  9. }
  10. private fun getView(parent: ViewGroup, layoutRes: Int): View {
  11. if (cacheMap[layoutRes] == null) {
  12. cacheMap[layoutRes] = LinkedList()
  13. }
  14. cacheMap[layoutRes]?.let { list ->
  15. if (list.isEmpty()) {
  16. viewCache(parent, layoutRes)
  17. } else {
  18. val view = list.poll()
  19. if (view != null) {
  20. viewCache(parent, layoutRes) // 拿一个补一个
  21. return view
  22. }
  23. }
  24. }
  25. return LayoutInflater.from(parent.context).inflate(layoutRes, parent, false) // 兜底处理
  26. }
  27. /**
  28. * 渲染布局至缓存,直到 cacheViewSize 为止
  29. */
  30. private fun viewCache(parent: ViewGroup, layoutRes: Int) {
  31. executors.execute {
  32. cacheMap[layoutRes]?.let { list ->
  33. while (list.size < cacheViewSize) {
  34. list.add(parent.getItemView(layoutRes))
  35. }
  36. }
  37. }
  38. }
  39. // ....
  40. open class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {}
  41. }

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/367980
推荐阅读
相关标签
  

闽ICP备14008679号