【问题标题】:RecyclerView with cursor loader is too slow when updating frequently频繁更新时带游标加载器的RecyclerView太慢
【发布时间】:2016-01-27 12:56:51
【问题描述】:

我正在从数据库中获取播客提要并使用 LoaderManager.LoaderCallbacks 在 RecyclerView 中显示它。在我的内容提供者中:

  1. 我在内容解析器 query() 方法中注册了一个“观察者”:cursor.setNotificationUri(getContext().getContentResolver(), uri);
  2. 当我在内容解析器中更新/插入/删除时,我会通知 Observer:getContext().getContentResolver().notifyChange(uri, null); 所以onLoaderReset() 会在需要时触发

然后我有一个带有 DownloadManager 的服务,它每秒更新我正在进行的下载到数据库的进度。加载器不断从数据库中加载数据,我可以在 UI 中看到每个下载片段的变化进度。

我认为,这是一种错误的通知更改方法,而且速度非常慢,但我现在想不出更好的解决方案。您能否建议任何使用 RecyclerView 和下载进度的有效解决方案?

我使用 RecyclerView 的自定义活动

public class MyActivity
    implements LoaderManager.LoaderCallbacks<Cursor> {

    private RecyclerView mRecyclerView;
    private MyCustomRecyclerViewAdapter mAdapter;

    ...

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        return new CursorLoader(this,
            myDataUri, null, null, null, null
        );  
    }   

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        mAdapter.swapCursor(data);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        mAdapter.swapCursor(null);
    }

    ...
}

我的自定义适配器

public class MyCustomRecyclerViewAdapter
    extends RecyclerView.Adapter<EpisodeAdapter.AudioAdapterViewHolder> {

    ...

    public void swapCursor(Cursor newCursor) {
        mCursor = newCursor;
        notifyDataSetChanged();
    }

    ... 
}

【问题讨论】:

    标签: java android android-recyclerview


    【解决方案1】:

    当适配器具有稳定的 id 时,ReclyclerView 效率更高(特别是 notifyDataSetChanged() 方法)。

    如果您的数据有 ID(例如来自数据库),您应该真正考虑让您的适配器使用 setHasStableIds(true) 并正确覆盖 getItemId(int position)

    我不确定这是否会完全解决您的问题,但这绝对是一个好习惯。

    【讨论】:

    • 由于限制,我无法使用setHasStableIds(true)“当适配器已注册观察者时,无法更改此适配器是否具有稳定的 ID。”。 ID没有改变,但相关数据是,所以不可能。不过谢谢你的建议。
    • setHasStableIds(true) 通常仅在构造函数中调用。稳定的id是指同一个item有相同的id,不代表数据(item内容)不变(这种优化可以在最近版本的RecyclerView中使用payload来处理)
    • 谢谢,我只是忘了实现getItemId(int position)...现在它可以工作了,但仍然很慢(由于光标切换)。
    • 我猜每秒刷新有点太快了。只是出于好奇,稳定的 ids 优化是否有帮助,或者根本没有帮助?
    【解决方案2】:

    不要使用notifyDataSetChanged(),而是使用notifyItemInserted()notifyItemRangeInserted() 以获得性能提升。

    对于已解决的类似问题,请查看at this

    【讨论】:

    • 使用 notifyItemChanged() 时 ViewHolder 闪烁,但仍然非常低效且滚动不流畅。这个想法是,每秒或每半秒交换整个光标并不好。我认为注册一些广播接收器来下载元素会更有效,但它必须在 onBindViewHolder() 中注册,但这也不是一个好习惯。
    • 任何工作而不会崩溃、看起来可以接受并完成工作恕我直言,这是一个很好的做法。
    • 所以当我在 onBindViewHolder 中为 ViewHolder 元素注册 BroadcastReceiver 以接收进度数据而不是从数据库中获取时,我什么时候可以取消注册是吗?
    • 当然在onPause()onFinish() 中。并在onResume()重新注册
    • 不,我们正在讨论 RecyclerView 适配器以及将接收器附加到列表中的元素 - 在 onBindViewHolder() 方法中,没有 onPause() 等...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-01
    • 1970-01-01
    • 2012-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多