RecyclerView滚动性能

fal*_*ojr 68 java performance android recycler-adapter android-recyclerview

我基于创建列表和卡片指南创建了RecyclerView示例.我的适配器只有一个模式实现来扩展布局.

问题是滚动性能差.这在RecycleView中只有8个项目.

在一些测试中我验证了在Android L中没有出现这个问题.但在KitKat版本中,性能的下降是显而易见的.

Gal*_*lya 189

我最近遇到了同样的问题,所以这就是我用最新的RecyclerView支持库做的:

  1. 使用新的优化ConstraintLayout 替换复杂布局(嵌套视图,RelativeLayout).在Android Studio中激活它:转到SDK Manager - > SDK Tools选项卡 - > Support Repository - >检查ConstraintLayout for Android和Solver for ConstraintLayout.添加到依赖项:

    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    
    Run Code Online (Sandbox Code Playgroud)
  2. 如果可能,使RecyclerView的所有元素具有相同的高度.并添加:

    recyclerView.setHasFixedSize(true);
    
    Run Code Online (Sandbox Code Playgroud)
  3. 使用默认的RecyclerView 绘图缓存方法并根据您的情况进行调整.您不需要第三方库来执行此操作:

    recyclerView.setItemViewCacheSize(20);
    recyclerView.setDrawingCacheEnabled(true);
    recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
    
    Run Code Online (Sandbox Code Playgroud)
  4. 如果您使用许多图像,请确保它们的大小和压缩是最佳的.缩放图像也可能会影响性能.问题有两个方面 - 使用的源图像和解码的位图.以下示例为您提供了如何解码从Web下载的图像的提示:

    InputStream is = (InputStream) url.getContent();
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = Bitmap.Config.RGB_565;
    Bitmap image = BitmapFactory.decodeStream(is, null, options);
    
    Run Code Online (Sandbox Code Playgroud)

最重要的部分是指定inPreferredConfig- 它定义将为图像的每个像素使用多少字节.请记住,这是首选方案.如果源图像有更多颜色,它仍将使用不同的配置进行解码.

  1. 确保onBindViewHolder()尽可能便宜.您可以将OnClickListener设置为一次,onCreateViewHolder()并通过接口调用适配器外部的侦听器,并传递单击的项目.这样您就不会一直创建额外的对象.在对视图进行任何更改之前,还要检查标志和状态.

    viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {
              Item item = getItem(getAdapterPosition());
              outsideClickListener.onItemClicked(item);
          }
    });
    
    Run Code Online (Sandbox Code Playgroud)
  2. 数据更改后,尝试仅更新受影响的项目.例如notifyDataSetChanged(),在添加/加载更多项目时,不要使整个数据集无效,只需使用:

    adapter.notifyItemRangeInserted(rangeStart, rangeEnd);
    adapter.notifyItemRemoved(position);
    adapter.notifyItemChanged(position);
    adapter.notifyItemInserted(position);
    
    Run Code Online (Sandbox Code Playgroud)
  3. 来自Android Developer网站:

依靠notifyDataSetChanged()作为最后的手段.

但是,如果您需要使用它,请使用唯一的ID维护您的项目:

    adapter.setHasStableIds(true);
Run Code Online (Sandbox Code Playgroud)

RecyclerView将尝试为适配器合成可见的结构更改事件,以便在使用此方法时报告它们具有稳定的ID.这有助于动画和视觉对象持久性的目的,但单个项目视图仍然需要反弹和重新传输.

即使你做的一切都正确,很可能RecyclerView仍然没有你想要的那么顺利.

  • 适用于adapter.setHasStableIds(true); 真正有助于快速完成Recyclerview的方法. (14认同)
  • @AbdelhakimAkodadi,使用缓存滚动变得平滑。我已经测试过了。怎么说呢,很明显。当然,如果有人像疯了一样滚动,没有任何帮助。我只展示了其他选项,如 setDrawingCacheQuality,我不使用它,因为图像质量对我来说很重要。我不宣扬 DRAWING_CACHE_QUALI‌ TY_HIGH,但建议任何有兴趣深入研究并调整选项的人。 (2认同)
  • setDrawingCacheEnabled() 和 setDrawingCacheQuality() 已弃用。而是使用硬件加速。https://developer.android.com/reference/android/view/View.html#setDrawingCacheEnabled(boolean) (2认同)

wac*_*law 14

我通过添加以下标志解决了这个问题:

https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#setHasStableIds(boolean)

  • 这使我的性能略有提升.但是RecyclerView仍然很慢 - 比同等的自定义ListView慢得多. (2认同)

Sco*_*ggs 11

我发现至少有一种模式可以扼杀你的表现.请记住,经常onBindViewHolder()调用.因此,您在该代码中执行的任何操作都有可能使您的性能停止.如果您的RecyclerView进行任何自定义,则很容易在此方法中意外地放入一些慢速代码.

我正在根据位置更改每个RecyclerView的背景图像.但加载图像需要一些工作,导致我的RecyclerView缓慢和生涩.

为图像创建缓存创造了奇迹; onBindViewHolder()现在只需修改对缓存图像的引用,而不是从头开始加载它.现在,RecyclerView拉链了.

我知道不是每个人都会遇到这个问题,所以我不打算加载代码.但请考虑在您的工作中所做的任何工作,onBindViewHolder()因为它可能会导致RecyclerView性能不佳.


bla*_*vla 10

除了@ Galya的详细答案之外,我想说明即使它可能是一个优化问题,启用调试器也可以减慢很多事情.

如果您要做所有事情来优化您的RecyclerView并且它仍然无法顺利运行,请尝试将构建变量切换到release,并检查它在非开发环境中的工作方式(禁用调试器).

在我看来,我的应用程序在debug构建版本中执行缓慢,但是一旦切换到release变体,它就能顺利运行​​.这并不意味着您应该使用release构建变体进行开发,但很高兴知道无论何时您准备好发布应用程序,它都可以正常工作.


小智 5

我不确定setHasStableIdflag 的用法是否能解决你的问题.根据您提供的信息,您的性能问题可能与内存问题有关.您在用户界面和内存方面的应用程序性能非常相关.

上周我发现我的应用程序泄漏了内存.我发现这是因为在使用我的应用程序20分钟后,我注意到UI的表现非常慢.关闭/打开活动或使用一堆元素滚动RecyclerView非常慢.在使用http://flowup.io/监控我的一些用户后,我发现了这个:

在此输入图像描述

帧时间真的非常高,每秒帧数真的很低.您可以看到某些帧需要大约2秒才能渲染:S.

试图找出导致这个错误的帧时间/ fps的原因我发现我有一个内存问题,你可以在这里看到:

在此输入图像描述

即使平均内存消耗接近15MB,同时应用程序正在丢帧.

这就是我发现UI问题的方式.我的应用程序中有内存泄漏导致大量垃圾收集器事件,这导致了糟糕的UI性能,因为Android VM必须阻止我的应用程序每帧收集内存.

查看代码我在自定义视图中发生了泄漏,因为我没有从Android Choreographer实例中取消注册侦听器.发布修复后,一切正常:)

如果您的应用因内存问题而丢帧,则应查看两个常见错误:

检查您的应用是否在每秒多次调用的方法内分配对象.即使这种分配可以在应用程序变慢的不同位置执行.一个示例可能是在回收器视图视图持有者的onBindViewHolder上的onDraw自定义视图方法内创建对象的新实例.检查您的应用是否将实例注册到Android SDK但未发布.将监听器注册到总线事件中也可能发生泄漏.

免责声明:我一直用来监控我的应用程序的工具正在开发中.我可以访问此工具,因为我是开发人员之一:)如果您想访问此工具,我们很快就会发布测试版!您可以加入我们的网站:http://flowup.io/.

如果您想使用不同的工具,可以使用:traveview,dmtracedump,systrace或集成到Android Studio中的Andorid性能监视器.但请记住,此工具将监控您连接的设备,而不是其他用户设备或Android操作系统安装.


Ole*_*ndr 5

我谈到了RecyclerView表演.这里有英文幻灯片俄语录制视频.

它包含一系列技术(其中一些技术已经被@Darya的回答所涵盖).

这是一个简短的总结:

  • 如果Adapter项目具有固定大小,则设置:
    recyclerView.setHasFixedSize(true);

  • 如果数据实体可以用long表示(hashCode()例如),那么设置:
    adapter.hasStableIds(true);
    和实现:
    // YourAdapter.java
    @Override
    public long getItemId(int position) {
    return items.get(position).hashcode(); //id()
    }
    在这种情况下Item.id()不起作用,因为即使Item内容已经改变它也会保持不变.
    PS如果您使用的是DiffUtil,则无需这样做!

  • 使用正确缩放的位图.不要重新发明轮子并使用库.
    更多信息如何在这里选择.

  • 始终使用最新版本RecyclerView.例如,25.1.0预取中有很大的性能改进.
    更多信息在这里.

  • 使用DiffUtill.
    DiffUtil是必须的.
    官方文件.

  • 简化您的项目布局!
    微小的库来丰富TextViews - TextViewRichDrawable

有关详细说明,请参见幻灯片