我有一个RecyclerView
,其适配器基于List
个项目。当适配器中有约400个元素时,在通知适配器已设置新数据时,主线程挂了大约1-2秒。
我已经跟踪了它,并且它只是在视图层次结构中很忙(从ViewRootImpl.performTraversal()
开始)。这没有任何意义,因为我希望它只是渲染实际上可见的10 ish元素,应该很快。
我是否缺少RecyclerView
的某些方面?
编辑:正在为列表中的每个元素调用onBindViewHolder()
。这似乎不正确。
编辑2: 这是适配器类: https://pastebin.com/8a2yLprh
这是项目布局: https://pastebin.com/J0DtnKwB
编辑3: 如果我给RecyclerView一个静态高度,就可以解决问题。本身不是解决方案,但希望能指明方向。
答案 0 :(得分:1)
我看到了一些问题,我将以不特定的顺序指出它们;尽管我还没有发现可能导致您问题的任何具体信息,但在编写此答案时,我可能会发现一些问题,因此让我们分析您的代码。
您没有包含更多代码,因此我不知道如何使用此适配器,或者recyclerview布局如何(它是否包含在另一个滚动视图或嵌套滚动视图中?) 这些是重要的问题,因为它可能会影响Android渲染/布局引擎的工作方式。
您正在使用带有权重的LinearLayout(此处不需要)。除非您有充分的理由,否则不建议使用这些方法。计算窗口小部件的大小以及不计算窗口小部件的大小是很昂贵的,并且在涉及图像和位图时更是如此。
我建议您切换到ConstraintLayout并做正确的事。您将感谢您,您的布局可能会表现更好(根据我的观察,ConstraintLayout的当前布局是不可能的)。
@Override
public void onClick(View v) {
Listener l;
if ((l = listener) != null) {
mainHandler.post(() -> l.onItemClick(devices.get(getAdapterPosition())));
}
}
总是应在主线程上进行单击,而无需发布任何内容。 我会这样写:
if (listener instanceof Listener) { //already fails if L is null anyway)
listener.onItemClick(...)
}
onBind
中执行了大量查找,如果您缓存了这些循环,则可以节省循环。例如:Picasso.with(context).load(SetupAppUtils.getDeviceImageUrl(context, device)).into(image);
我不知道您的SsetupAppUtils.getDeviceImageUrl
方法做什么,但是它可能很昂贵,并且您可以根据需要将其保存在Map中(一次查询URL,重复使用)(仅是个主意,无法分辨)无需查看代码)
您似乎在onBind方法中嵌入了许多业务逻辑(很多if
语句通常是一个危险信号。您的适配器对此一点也不在乎,它只是“从模型->视图中调整数据,如果可以避免,则不应做出决定并执行转换。在这种情况下,我看到您通过了Device
...似乎还可以,但是您似乎在做很多事情关于检查和转换,请记住,一旦绑定所有项目,这都会发生……,每次用户滚动并需要绑定新项目时,只要需要,您就再次执行所有这些工作来“重新绑定”视图持有者。
您要通知完整的更改,这意味着要为recyclerview分配一个批号(必须重新测量并重新布置),因为您实际上是在告诉适配器:所有数据都已更改。 / p>
void setDevices(List<Device> devices) {
this.devices.clear();
this.devices.addAll(devices);
notifyDataSetChanged();
}
使用DiffUtil(包含在框架中!)非常简单,因此完全推荐使用。
@Override
public long getItemId(int i) {
return 0;
}
如果您无法提供良好的ID,请不要覆盖此ID。我将返回item [i] .something()(在此之前检查null /空列表!)。还要将i
重命名为position
,因为这样就可以了。
在这些方面,我看不出有什么超级奇怪的。我将做一个简单的测试:删除那里所有的绑定代码(onBind中的所有代码),然后放入name.SetText(...)
,看看是否仍然看到“滞后”。
如果不这样做,那么您就会知道其中一些操作所花费的时间比其他操作要长。
尝试较少的物品,看看会发生什么;一个recyclerview应该只绑定可见的视图+/-几个上/下,而不是一次绑定400个项目。 我能想到的唯一原因是,如果您的RecyclerView位于NestedScrollView或类似的容器内。
答案 1 :(得分:0)
要一次加载的400个项目太多,您应该使用 Pagination 机制。Google现在将paging library作为android Jetpack的一部分。另外,您会发现很多文章和教程,讨论如何在android中实现它。
答案 2 :(得分:0)
正在回答我自己的问题(带有一些补充评论)...不幸的是,我仍然不完全了解其根本原因。当我将RecyclerView
的高度设置为固定的值(例如"400dp"
)时,问题自行解决。显然,尽管这不是一个适当的解决方案,但它使我开始了。
我尝试的任何布局都不能解决问题。最终,我将所有内容都转换为使用ConstraintLayout
,并且在对布局的宽度和高度进行调整之后,我发现了可以解决该问题的方法。这与使用"0dp"
作为高度有关。不好意思我不了解根本原因。我在下面粘贴了布局,以防它对任何人都有帮助。
总的来说,我对ConstraintLayout
感到不寒而栗。这非常冗长,并且约束范式(相对于我想您称之为遏制)并不自然。例如,需要使用浮动Group
元素来显示和隐藏其他逻辑组,并使用“链”将元素组居中。
具有讽刺意味的是,在旧学校LinearLayout
上一切正常。我将其作为学习活动进行。您必须怀疑使用传统的LinearLayout
+ ListView
和ConstraintLayout
+ RecyclerView
的平均布局对真正的性能影响是什么。
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<ProgressBar
android:id="@+id/progress_bar"
style="@android:style/Widget.Holo.ProgressBar.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="visible"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
/>
<android.support.constraint.Group
android:id="@+id/content_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible"
app:constraint_referenced_ids="header,list_view"
/>
<android.support.constraint.ConstraintLayout
android:id="@+id/header"
android:layout_width="0dp"
android:layout_height="48dp"
app:layout_constrainedHeight="true"
android:background="@color/gray_20"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/list_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
>
<View
android:id="@+id/device_image"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/device_name"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/device_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/name_label"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_weight="2"
app:layout_constraintLeft_toRightOf="@id/device_image"
app:layout_constraintRight_toLeftOf="@id/device_model"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/device_model"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/model_label"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_weight="2"
app:layout_constraintLeft_toRightOf="@id/device_name"
app:layout_constraintRight_toLeftOf="@id/device_id"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/device_id"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/id_label"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_weight="2"
app:layout_constraintLeft_toRightOf="@id/device_model"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/list_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:fadingEdgeLength="@dimen/fadingEdgeLength"
android:footerDividersEnabled="false"
android:requiresFadingEdge="vertical"
app:layout_constraintTop_toBottomOf="@id/header"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
/>
</android.support.constraint.ConstraintLayout><?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<ProgressBar
android:id="@+id/progress_bar"
style="@android:style/Widget.Holo.ProgressBar.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="visible"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
/>
<android.support.constraint.Group
android:id="@+id/content_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible"
app:constraint_referenced_ids="header,list_view"
/>
<android.support.constraint.ConstraintLayout
android:id="@+id/header"
android:layout_width="0dp"
android:layout_height="48dp"
app:layout_constrainedHeight="true"
android:background="@color/gray_20"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/list_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
>
<View
android:id="@+id/device_image"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/device_name"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/device_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/name_label"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_weight="2"
app:layout_constraintLeft_toRightOf="@id/device_image"
app:layout_constraintRight_toLeftOf="@id/device_model"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/device_model"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/model_label"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_weight="2"
app:layout_constraintLeft_toRightOf="@id/device_name"
app:layout_constraintRight_toLeftOf="@id/device_id"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/device_id"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/id_label"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_weight="2"
app:layout_constraintLeft_toRightOf="@id/device_model"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/list_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:fadingEdgeLength="@dimen/fadingEdgeLength"
android:footerDividersEnabled="false"
android:requiresFadingEdge="vertical"
app:layout_constraintTop_toBottomOf="@id/header"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
/>
</android.support.constraint.ConstraintLayout>