Android 性能优化之RecycleView的性能优化原则

Android 性能优化之RecycleView的性能优化

一、概述

RecyclerView有着极高的灵活性,能实现ListView、GridView的所有功能,也能轻松实现ListView、GridView不易实现的功能,如多 Type 布局列表。在日常开发中,RecyclerView使用非常广泛,如果使用不当将会出现闪烁、卡顿、占用内存过高等问题,影响应用性能,也会影响用户体验,所以有必要了解一下RecyclerView的性能优化方法。

二、RecycleView的性能优化原则

1.数据处理和视图加载分离

  数据处理的一些耗时逻辑可以考虑放在异步里面处理,这样Adapter在notify change后,ViewHolder就可以简单无压力的做数据与视图的绑定逻辑。比如:

mTextView.setText(Html.fromHtml(data).toString());

这里的 Html.fromHtml(data) 方法可能就是比较耗时的,存在多个 TextView 的话耗时会更为严重,这样便会引发掉帧、卡顿,而如果把这一步与网络异步线程放在一起,站在用户角度,最多就是网络刷新时间稍长一点。

2.数据优化

  • 分页拉取网络数据
  • 同时对拉取下来的数据进行缓存,提升再次加载速度;
  • 对于新增或删除等数据变化情况通过DiffUtil(DiffUtil是support-v7:24.2.0中的新工具类,它用来比较两个数据集,寻找出旧数据集-》新数据集的最小变化量。 DiffUtil会自动计算新老数据集的差异,并根据差异情况,自动调用RecycleView的adapter的四个方法更新数据)来进行局部刷新数据,DiffUtil刷新数据时会有动画效果,而且比全局刷新效率高。

使用DiffUtil,代码如下:

DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCallBack(mDatas, newDatas), true);
diffResult.dispatchUpdatesTo(mAdapter);

  DiffUtil会自动计算新老数据集的差异,并根据差异情况,自动调用以下四个方法:

adapter.notifyItemRangeInserted(position, count);
adapter.notifyItemRangeRemoved(position, count);
adapter.notifyItemMoved(fromPosition, toPosition);
adapter.notifyItemRangeChanged(position, count, payload);

3.recyclerView.setHasFixedSize(true);

  当RecyclerView的Item的高度固定时,设置这个选项可以提高性能,器原理就是要避免整个布局绘制,即避免requestLayout。

4.布局优化

  减少布局层级,防止出现过度绘制,如可以考虑用ConstraintLayout或者自定义View替代ItemView。用 ConstraintLayout 可以最大程度减少层级。

  布局优化的另一种手段是采用标签、标签和ViewStub:

  1. 标签:如果当前布局和包含的布局中都是竖直方向,那么使用标签可以去掉多余的LinearLayout,一般和标签一起使用,从而减少布局的层级;
  2. 标签:布局重用,不用把已经写过的布局重新写一遍,而且可以把同一个布局中用到的重复布局抽离出来,使用时用include,布局重用是代码复用的思想;
  3. ViewStub:继承view,非常轻量级且宽高都是0,自己不参加任何布局的绘制过程,提供了按需加载功能,当需要时才将ViewStub中的布局加载到内存,这提高了程序的初始化效率。

5.减少xml文件inflate时间

  这里的 xml 文件不仅包括 layout 的 xml,还包括 drawable 的 xml,xml 文件 inflate 出 ItemView 是通过耗时的 IO 操作,尤其当 Item 的复用几率很低的情况下,随着 Type 的增多,这种 inflate 带来的损耗是相当大的,此时我们可以用代码去生成布局,即 new View() 的方式,只要搞清楚 xml 中每个节点的属性对应的 API 即可。

6.减少View对象的创建

  一个稍微复杂的 Item 会包含大量的 View,而大量的 View 的创建也会消耗大量时间,所以要尽可能简化 ItemView;设计 ItemType 时,对多 ViewType 能够共用的部分尽量设计成自定义 View,减少 View 的构造和嵌套。

7.用RecycledViewPool复用

  如果多个RecycledView的 item 相同,比如可以滑动的tab页面,如果每个页面的 ViewHolder 都是一样的,就可以用共享一个对象池RecycledViewPool,代码如下:

RecyclerView.RecycledViewPool sharedPool = new RecyclerView.RecycledViewPool();
RecyclerView.setRecycledViewPool(sharedPool);

  RecycledViewPool是依据ItemViewType来索引ViewHolder的,所以不同页面的相同的item的type必须是一样的值才能被准确的复用。

  RecycledViewPool的核心思想是多个RecycledView共用 ViewHolder,多个tab切换时,可以减少后面RecyclerView调用onCreateViewHolder的次数。为了防止多个tab快速切换时出现的卡顿问题,可以考虑提前创建ViewHolder,用空间换时间,在创建ViewHolder的时候就可以直接从池里获取,而不用再去 createViewHolder。参考代码如下:

RecyclerView.RecycledViewPool sharedPool = new RecyclerView.RecycledViewPool();
MyAdapter myAdapter = new MyAdapter(datas);
RecyclerView.ViewHolder viewHolder = myAdapter.createViewHolder(recyclerView, 0);//先把 sharedPool 和 ViewHolder 创建出来
pool.putRecycledView(viewHolder);
//下面是真正创建recyclerView
recyclerView.setRecycledViewPool(sharedPool);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(myAdapter);

8.可选项:升级 RecycleView 版本到 25.1.0 及以上使用 RecyclerView 的预取 Prefetch 功能

  Prefetch 功能对视图复杂度比较高的情形,改善效果比较明显,可通过GPU呈现模式分析-打开的GPU渲染条形图进行对比测试。
  如果你使用 RecyclerView 提供的默认 layout manager,你将自动获得这种优化。然而,如果你使用嵌套 RecyclerView 或者自己写 layout manager,你需要改变你的代码来利用这个特性。
  对于嵌套 RecyclerView 而言,要获取最佳的性能,在内部的 LayoutManager 中调用 LinearLayoutManager 的 setInitialItemPrefetchCount()方法(25.1版本起可用)。例如,如果你竖直方向的list至少展示三个条目,调用 setInitialItemPrefetchCount(4)。
  如果你实现了自己的 LayoutManager,你需要重写 LayoutManager.collectAdjacentPrefetchPositions()方法。该方法在数据预取开启时被 RecyclerView 调用(LayoutManager 的默认实现什么都不做)。第二,在嵌套的内层 RecyclerView 中,如果你想让你的 LayoutManager 预取数据,你同样应当实现 LayoutManager.collectInitialPrefetchPositions()。

9.其他方面

  • 通过设置setItemViewCacheSize增加RecyclerView的缓存,用空间换时间提高滑动列表时的流畅性;
  • 对 ItemView 设置监听器,不要对每个 Item 都调用 addXxListener,应该共用一个 XxListener,根据 ID 来进行不同的操作,减少了对象频繁创建带来的资源消耗;
  • 通过 getExtraLayoutSpace 来增加 RecyclerView 预留的额外空间(显示范围之外,应该额外缓存的空间);
  • 设置 RecyclerView.addOnScrollListener(listener); 来对滑动过程中停止加载的操作;
  • 如果不要求动画,可以通过 ((SimpleItemAnimator) rv.getItemAnimator()).setSupportsChangeAnimations(false); 把默认动画关闭来提升效率;
  • 通过重写 RecyclerView.onViewRecycled(holder) 来回收资源;
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页