【问题标题】:Lagging in RecyclerView at very first scrolling在第一次滚动时滞后于 RecyclerView
【发布时间】:2021-05-11 20:04:44
【问题描述】:

我显示设备的联系人列表。在我的带有 Android 版本 9 的三星 Galaxy S8 中,当我第一次滚动 recyclerView 时,它并不流畅并且有点滞后。但随后它开始非常顺利地滚动。如果我使用后退按钮关闭应用程序并再次启动应用程序,它会再次平滑滚动,但如果我从最近的历史记录中销毁应用程序实例,然后再次启动应用程序,它会不平滑并且在第一次滚动时有点滞后。 (我在Google Pixel2中测试过,当buttery低时,我感觉和我解释的一样。)

这是 Galaxy s8 中问题的记录屏幕:https://drive.google.com/file/d/1szfF1oKEYZK3LIqQHC-MsapRxdJ85bFy/view?usp=sharing

我尽可能优化了recyclerView 适配器,似乎问题与我的适配器无关。你可以在这里查看源代码:https://github.com/AliRezaeiii/DignityContacts

我有一个自定义的 CoordinatorLayout.Behavior 来隐藏/显示 AppBarLayout 以及 bottomBar。我确信这与此无关,因为当没有 CoordinatorLayout 行为时,它会再次显示滞后,正如我上面解释的那样。

这是我的 recyclerView 项目布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:background="@android:color/white" 
    android:orientation="vertical">
    
    <include layout="@layout/contact_separator" />
    
    <include layout="@layout/contact_detail" />
    
    <LinearLayout
        android:id="@+id/subItem"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical" />
    
    </LinearLayout>

contact_separator 和 contact_detail 都是 ConstaintLayouts。

这是我设置 recyclerView 的方法:

mAdapter = new ContactsAdapter();
binding.recyclerView.setHasFixedSize(true);
binding.recyclerView.setAdapter(mAdapter);

final Observer<Resource<List<Contact>>> contactsObserver = resource -> {
            if (resource instanceof Resource.Success) {
                mContacts = ((Resource.Success<List<Contact>>) resource).getData();
                mAdapter.setItems(mContacts, true);
            }
        };

这可能是什么原因?

【问题讨论】:

    标签: android android-recyclerview android-adapter


    【解决方案1】:

    如果不运行项目很难分辨,但快速浏览一下(浏览您的适配器代码只需 2-3 分钟),就会有一些“气味”。

    1. 您使用的是普通的 RecyclerView.Adapter,您应该/可以使用 ListAdapter&lt;T, K&gt; 和 DiffUtilCallback 以避免在执行 setList(...) 时使用 notifyDataSetChanged()(相反,您只需执行 submitList(...) 和让适配器为您解决问题。

    2. 您的bind(...) 方法相当复杂且冗长;有两个潜在的循环和视图参数更改(小部件尺寸),这将导致 至少 测量传递(随后将是布局传递),所有这些都发生在视图持有者中的每个项目上, 当您提交列表时。当应用程序被创建时(在它被销毁之后),所有这些都必须(再次)计算,所以回收器视图,它有一个 setFixedSize = true (你为什么需要这个,考虑到你的适配器,这不是真的)。如果 fixed Size 为真,则避免 requestLayout() 调用,但这意味着您告诉适配器它的大小是恒定的(意味着它可以根据项目数增长/缩小,但所有项目大小相同)。这是一种优化,但它可能会反噬你,而且很可能不是你想要的。

    3. 您的flagItems 是一个LinearLayout,您可以在运行时在bind 方法中动态地向这个线性布局添加/删除视图。

    4. 你对subItem(另一个线性布局)做同样的事情。

    5. 然后再次可能会出现另一个循环(联系人号码),其中布局可能会再次被修改(约束集已更改)。

    所有这些(还有一些其他细节),但现在可以这样做......在适配器方面是“危险信号”。

    有趣的是,所有这些都应该在 android 渲染帧所需的时间更少(60 毫秒?)。所以你要求代码做很多工作并触发很多副作用,并期望它在 60 毫秒或更短的时间内完成。这适用于每个被绑定的视图(取决于设备的屏幕尺寸),所以想象 5 个项目适合屏幕; RecyclerView 将在您滚动时提前绑定更多内容。

    你能做什么?

    我会先退回您的List&lt;Contact&gt; contacts。这些联系人可以更好地转换以在 RecyclerView 上呈现。所有关于显示什么/如何显示的逻辑都应该提前解决,并以最扁平/最简单的可能形式呈现给适配器(已经有其他工作要做)。您可以使用 ViewHolders 的 type 来发挥自己的优势,方法是将数据分解为不同的类型,并让适配器简单地为每种视图类型绑定正确的 viewHolder。

    因此,您将提供更适合 viewHolder 的 List&lt;SomeOtherObject&gt; 而不是 List&lt;Contact&gt;,并且只包含您稍后重新获取原始 Contact 所需的数据(如果需要),或重建它。

    然后您可以简化并删除 viewHolder#bind 方法中的所有逻辑/决策,因为当您确定要绑定哪种类型的数据时,很多问题都已解决。

    我会从那里开始,因为您可以尝试优化所有您想要的,但您仍然要求适配器为您完成所有这些工作。

    为什么当您“不杀死应用程序”时不会发生这种情况?

    那是因为(我估计)这些东西被缓存和预先计算(他们已经第一次花费了时间),所以因为你有 FixedSize = true,RecyclerView 知道(被告知)每个 ViewHolder 的大小不会改变,所以不需要重新计算。

    简而言之,您并没有让您的适配器性能更高,您只是告诉他花这么多时间一次。 ;)

    【讨论】:

    • 感谢您审阅代码、分析它们以及关于使用 ViewHolders 类型而不是我在 bind 方法中的复杂逻辑的一个很好的建议。如果我没有将 FixedSize 设置为 true,则可能每次都会发生滞后。你同意吗?
    • 是的,但是如果您有动态大小的查看器(其中的内容将/可能影响视图的大小),那么您不能将其设置为 true,否则您的视图无论内容如何,​​都将具有相同的大小(ViewHolder 高度/宽度),并且内容将被剪裁。
    • 我将 FixedSize 设置为 false 但在第一次和我杀死应用程序时再次发生滞后,尽管您对此的描述似乎合乎逻辑。所以我不知道原因。
    • 尝试通过bind 方法评论内容。就像只绑定“一个文本”并评论其他所有内容。这执行得快吗?您在哪里(在生命周期中)设置数据?
    • 我评论了所有内容,奇怪的是我在 Galaxy s8 中再次感到第一次滚动时的滞后。我的 ViewModel 中有一个 liveData,我在 onCreateView 中观察到它,并在传递结果时设置数据。您可以在我的问题末尾查看代码。
    【解决方案2】:

    你应该用一个列表初始化适配器,目前你正在设置一个观察者,当它被观察到时,列表就会被初始化,这就是它滞后的原因。

    【讨论】:

    • 我有一个后台作业,我需要使用观察 LiveData 等到数据准备好。在传递数据时,我无法想象比 submitList 更好的解决方案。如何提前初始化适配器?
    猜你喜欢
    • 2021-08-14
    • 2017-06-14
    • 2023-04-06
    • 2018-01-30
    • 1970-01-01
    • 2014-10-08
    • 2014-02-15
    • 1970-01-01
    相关资源
    最近更新 更多