Android:在调试中使用 RecyclerView 进行初始滚动时卡住

Ros*_*mov 5 performance android android-recyclerview

我有一个简单的测试应用程序,它具有类似社交媒体的布局: 在此处输入图片说明

它由用于帖子的外部回收器视图和用于评论的嵌套 RV 组成(屏幕截图上有缩进)。

问题: 主要是在调试模式下,只有在初始滚动发生时才会出现相当大的卡顿(可能是 RV 的一些初始化内容)。进一步滚动在调试和发布中都是平滑的。在发布中,它主要发生在使用一些密集型应用程序之后,例如在 Facebook 应用程序上观看视频。如果它发生在发布中,那么卡顿就不那么明显了。 初始滚动发生时的 Systrace

设置详情

电话 - Nexus 5X 与 Android 8.1,compileSdkVersion - 28,minSdkVersion - 16,targetSdkVersion - 28

相关代码如下:

外部 RV 的适配器- 在这里,我使用带有评论的帖子数据以及嵌套的 RV 池,以便嵌套的 RV 可以利用它。视图持有者只是在视图中存储一些对不同控件的引用。onCreateViewHolder是我尝试一些优化的地方,包括预创建 10 个视图持有者(当前代码)。我已将setHasFixedSize设置为 true,但这没有帮助。RV 的布局管理器是LinearLayoutManager,没有任何自定义。

if (viewHolders.size == 0) {
        //Log.w("msg", "creating all view holders")
        for (i in 1..10) {
            val v = LayoutInflater.from(parent.context)
                    .inflate(R.layout.wallpost_view, parent, false) as LinearLayout
            val viewHolder = MyViewHolder(v, context, nestedViewsPool)
            viewHolders.push(viewHolder)
        }
    }
Run Code Online (Sandbox Code Playgroud)

内部 RV 的适配器- 这里没有什么特别之处,因为它目前不包含任何自定义事件。

外部 RV 项目的视图-是的,它使用嵌套布局,但在将其转换为 ConstraintLayout, CL 版本时,我没有看到任何性能差异。

内部 RV 项目的视图- 这里没有什么特别之处,看起来与外部项目的视图相似,但没有按钮和链接。CL 版本在这里

什么会导致卡顿?自从我最初使用简化的视图和代码进行测试以来,它就一直存在(在调试中)。

Fab*_*bio 5

需要注意的几件事

1-不在调试模式下测量性能

我怎么强调都不为过。在不附加调试器的情况下运行,您将看到性能的巨大改进。

2-扁平化你的设计

不要使用每个项目都有一个 RV 的 RV,而是使用具有不同单元格的单个 RecyclerView。因此,第一个单元格将是“墙贴内容”,第二个单元格将是“墙贴答案 1”,第三个“墙贴答案 2”等。

我一直在努力让我的一个屏幕表现得更好,其中我们有水平 RV(具体来说是 viewPager 2)和嵌套垂直 RV,我追逐了许多死胡同,最终不得不采取一些技巧,例如禁用预加载上一页/下一页,直到当前页面加载完毕。但在初始加载后它不会挣扎。

3- 进行<include布局不太可能使其变慢。

但是嵌套层次结构确实如此,例如:

<LinearLayout> 
  <LinearLayout>
    <LinearLayout>
Run Code Online (Sandbox Code Playgroud)

考虑将所有内容重写为单个 ConstraintLayout。你的用户界面并没有那么复杂。删除一些东西,对其进行测量,如果明显改善则进行转换。我会从内部布局开始,因为单个屏幕上可能有更多布局?

最后

4-小心日期转换

我注意到 ThreeTenAbp 中的一些方法消耗了不合理的时间。尝试跳过所有这些方法,用虚拟字符串替换它,并检查它是否有任何区别。如果是的话,你有几个选择,但这是我最初的两个怀疑

  • 仅初始化日期格式化程序一次,并在后台线程中使用它们
  • 检查您的日期解析器是否在后台线程中运行

如果 Java 8 DateTime 也有同样的问题,我不会感到惊讶。

===============

写完以上所有内容后,我查看了您的 systrace。从快速看来,罪魁祸首似乎是嵌套的线性布局。我可以看到大约 1.880 毫秒,有一个很长的膨胀,需要 30 毫秒,这是一个很大的危险信号。它内部有 3 个线性布局,它们往往指向内部或外部布局的大致方向。所以我会先尝试(3),如果还不够,我会尝试(2)。

============== 编辑:添加(5)

5-为内部recyclerview共享一个RecycledViewPool

创建 RecycledViewPool 类型的单个对象并将其分配给所有内部 recyclerview。
https://developer.android.com/reference/androidx/recyclerview/widget/RecyclerView.RecycledViewPool


Ros*_*mov 1

我会将其作为答案发布,因为它满足了我的要求 - 它减少了第一次滚动所需的时间。

我在调用活动中移动了两种类型的视图持有者的预创建:

for (i in 1..30) {
    val v = LayoutInflater.from(parent.context).inflate(R.layout.wallpost_view, parent, false) as LinearLayout
    val viewHolder = MyViewHolder(v, context, nestedViewsPool)
    viewHolders.push(viewHolder)
}

for (i in 1..20) {
    val v = LayoutInflater.from(this).inflate(R.layout.comment_view, null) as LinearLayout
    val viewHolder = WallpostsListAdapter.CommentsViewHolder(v)
    adapter.commentViewHolders.push(viewHolder)
}
Run Code Online (Sandbox Code Playgroud)

在我进行的大多数测试中,这使得第一次滚动速度加快了 2 倍。这一行动在优化过程中带来了最大的收益。

我也采纳了@Fabio的部分建议:

1.正如我在评论中所说,我在调试模式下测量这些东西,以便能够更好地看到优化结果。当然,在发布版本中,以毫秒为单位的数字会小得多(实际上比我在发布版本的 systrace 中看到的小 10 倍)。

2.这是造成最大差异的一点,虽然差异不大,但仍然可以在 systrace 中发现。检查答案末尾的pastebins以获取更多信息。

3.事实上,Google 建议使用ContraintLayout而不是嵌套布局,但就我而言,这没有任何区别。实际上,嵌套线性布局似乎显示出稍微更好的结果。我的猜测是,视图不够复杂,无法看到 CL 的收益。我无法使用嵌套线性布局测试最终优化的代码,因为我现在使用单个多类型视图 RV 并且它们无法正常工作,无法找到原因,但可能不会获得增益如此重要。

4.好点,但至少对我来说没有任何重大影响。

5.已过时,因为现在只有一个 RV,没有嵌套。

相关代码

  • RV 的适配器- 现在是一个多类型视图适配器,可管理帖子和评论。最大的变化是我需要编写 RV 位置和数组索引之间的映射代码。现在在 init 上我需要进行映射。在评论的 VH 绑定中,我首先需要获取其父帖子的 RV 位置,从那里获取数组中的索引,最后从它们的差异中获取帖子评论数组中评论的索引。
  • 使用 ConstraintLayout 的帖子视图
  • CL评论的看法

系统跟踪

  • 带有 CL 的视图、所有视图持有者预先创建、单个 RV -此处
  • 带有 CL 的视图,仅发布预先创建的 VH,单个 RV -此处