RecyclerView优化笔记

RecyclerView优化算是在玩物得志算是比较有意思也有挑战性的任务了,也算是面试一个常见和比较加分的项目,所以在这里纪录一下。

RecyclerView为什么卡顿

卡顿一定要先知道是什么引起的卡顿,才能解决。RecyclerView卡顿说到底就是两个点,一个是渲染本身消耗了太多的时间,第二个就是gc。其实现在安卓技术的普遍提高,像在主线程做耗时操作这种事情应该是做不出来了,但是还是要想到这一方面。

瞬间创建大量对象

在社区推荐列表的埋点曝光中就遇到了这个问题,我在onViewBind这个生命周期中创建map,结果造成了内存抖动,造成了卡顿。

渲染本身消耗时间

LayoutInflate.from(context).inflate()这个方法本身就是比较耗时的,如果页面嵌套比较多的话,那么渲染耗费的时间就更长了。

gc

gc这个是关于JVM的知识点,关键就是GC的时候会stop the world,使所有的线程都停止,保证GC的完成。

耗时操作

这个就不用说了,网络请求就是一个耗时操作,在主线程中进行一个网络请求就会把主线程卡死。

怎么会造成卡顿

知道了卡顿的原因,我们接下来分析怎么样才会造成这样的卡顿。

什么会造成渲染时间更长

渲染是不可避免的,所以我们要了解渲染的流程,知道什么样会造成渲染时间变得更长了。我们的页面通常是通过XML来确定布局,然后通过LayoutInflate.from().inflate()来解析XML,最后在每一次addView 进行requetLayout()requestLayout()会将重新从根布局走一次绘制流程 onMeasure -> onLayout -> onDraw。

当页面出现过多嵌套的时候,页面卡顿的时间就会更长

什么会操作gc

gc无时不刻的在执行,因为我们在无时不刻的在制造对象。了解jvm的内存回收机制的话会知道,gc也分为两种,一种只是新生代的gc,这种gc是比较低耗时的。另外一种老生代的gc,也被成为full gc,这种gc相对于新生代gc要消耗更多的时间,而上面讲到了gc是stop the world的,是会卡死所有线程的。

当我们创建了过多或者过大的对象,都会使两种gc出现的频率变得更大。

耗时操作和内存抖动我们就不需要再分析了,因为他们本身就是造成卡顿的行为了。

如何避免呢

既然都已经知道了为什么卡,怎么造成的卡顿,那么就有了优化的方向。再加上对RecyclerView的理解,就能比较全面的优化了。

减少Adapter View中的嵌套

经过上面的分析,已经很好理解减少Adapter View嵌套了,如果减少嵌套呢,那就是用ConstraintLayout了,或者使用merge、viewStub这些东西。但是网上说Adapter结合ConstraintLayout会出现卡顿,我暂时是没有遇到这个问题。

####减少gc

减少在 onBindViewHolder中创建对象,能复用的对象,尽量放在Adapter的构造方法或者在onCreateViewHolder中,这样可以避免每次onBindViewHolder调用的时候重复创建类。

RecyclerView中和图片或者是视频是根本分不开的,而图片在安卓中永远都是大内存的东西。所以在获取的图片的时候,一定要做一些压缩。

项目中目前的图片库基本都是Glide,要了解Glide的缓存机制和连接池和Bitmap池,做好缓存策略。

RecyclerView缓存机制

简单介绍一下RecyclerView的缓存机制。RecyclerView的缓存分为4层,

第一层是mCacheView,缓存的item不会调用onCreateViewHolder也不会调用onBindViewHolder,他只是保存被划上去的2个item。

第二层是让用户自己扩展的。

第三层是RecyclerPool,他会一个SpareArray,key值是ViewType, value值是一个ArrayList,保存相应的ViewHolder。这一层是RecyclerView缓存的关键。这个缓存能让RecyclerView只在第一次创建ViewHolder,之后只要RecylerPool中对应的ViewType的ArrayList的ViewHolder能撑起整个页面的ViewItem,那么他就不会再创建ViewHolder,也就不会再创建View,这样就能有流畅的滑动体验了。

总结一下

总结一下这次我在玩物优化RecyclerView的过程,第一个是在优化addOnScrollListener,将这里不需要重复创建的类抽出来。第二个是onBindViewHolder中的优化,也是抽出重复创建的类。第三个就是获取低一点质量的图片。第四就是设置RecyclerPool的某个itemView的Viewholder的size变大,大到一定能撑起一个屏幕的item。