记一次recyclerview的坑

1.背景

最近的项目需求中,有个不同于往常的交互需求:

页面分为上下2部分,上面文档类型,可以点击加载更多向下加载一页数据,加载完数据,加载更多会变成收起按钮,可以将列表收起到5个 item 长度。下面知识库类型,可以滑到底部,进行上拉加载更多。

2个部分是2个 http 接口返回的数据,数据之间无任何关联,在功能相对独立的基础上,思考了一下,决定用2个 RecyclerView 实现。

2.NestScrollView 嵌套 RecyclerView

前期实现界面布局如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">

<androidx.core.widget.NestedScrollView
android:id="@+id/recyclerview_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@color/L_11"
android:fillViewport="true"
android:scrollbars="none"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/divider">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<SearchRefreshListView
android:id="@+id/search_list"
style="@style/globalSearchListView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

<View
android:id="@+id/separator_view"
android:layout_width="match_parent"
android:layout_height="6dp"
android:background="#F7F8FA" />

<SearchRefreshListView
android:id="@+id/search_knowledge_ku_list"
style="@style/globalSearchListView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>

</androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

数据处理过程就略过了,一开始运行起来效果都很完美,交互也完全满足要求,但是在 qa 测试阶段,确出现了 ANR。。。根据 qa 描述,尝试复现 ANR 场景,发现只要加载20几页,大概600条左右数据的时候,稳定复现 ANR。。。

3.ANR 追踪

出现 ANR,第一反应就是看本地日志,通过 adb bugreport 取出本地 ANR 日志,发现每次 ANR 日志都不一样,

下面的 ANR,能够看出是在等待 gc 的过程 出现 ANR,而且有本地的代码栈,猜想可能是 bindView 执行了耗时操作,review 了一下代码,发现都是简单的绘制,而且 item 数据集也小,不大可能是绘制问题。

毫无头绪之下,想用 as 的 profiler 看一下应用的 cpu 和内存是否都正常,结果一看,发现每次加载内存持续要增长,一直没有下降过,于是转而观察 cpu 在每次下拉加载时都做了什么操作,一查发现,在 mian 线程里面adapter 的 bindview 确实是执行了很多次,才初步考虑到,可以是 RecyclerView 没有复用, 导致每个 item 都要绘制。

在 Adapter 的onBindViewHolder(@NonNull ViewHolder viewHolder, int i)中,打印出 i来验证这个想法,看到每次下来,从0开始的 i 都会被打印出来,证明 view 确实没被复用,所以 item 多时,会导致 ANR。顺着这个思路,在 google 上搜索的一下,看到一个文章 实名反对《阿里巴巴Android开发手册》中NestedScrollView嵌套RecyclerView的用法,坐实了 NestScrollView 嵌套 RecyclerView 的问题,至于为什么会复用失败,文章里面有相信的介绍,我就多说了。最终去掉 NestScrollView 嵌套,直接使用 RecyclerView 的多种 type,实现了上述的交互效果。

4.总结

这是一个小白,第一次尝试解 ANR 问题,以前遇到 ANR 都是看着日志文件无从下手,通过此次,学习到了一个看 ANR 日志,看 profiler 信息的技能,虽然用了很长时间,也算是有所收获。(这里还要感谢某人工作繁忙的时候还要教我如果看追踪 ANR,(ღ˘⌣˘ღ)..)

0%