RecyclerView blocking ui thread during updates

旧时模样 提交于 2019-12-11 01:49:09

问题


There are more than 200 items in my list. RecyclerView is updated regularly (Every 10 seconds) . RecyclerView blocking ui thread for few seconds during updates. I'm using notifyDataSetChanged method for refresh recyclerview. Is there another way to prevent freezing ? By the way I don't want use pagination.

This method run every 10 seconds :

public void refreshAssetList(List<Asset> newAssetList){
     recyclerViewAdapter.setAssetList(newAssetList);
     recyclerViewAdapter.notifyDataSetChanged();
}

RecyclerViewAdapter class :

public class AssetListRecyclerViewAdapter extends RecyclerView.Adapter<AssetListRecyclerViewAdapter.BaseViewHolder> {

    private List<Asset> assetList;
    Context context;

    public AssetListRecyclerViewAdapter(List<Asset> assetList, Context context) {
        this.assetList = assetList;
        this.context = context;
    }

    public void setAssetList(List<Asset> assetList) {
        this.assetList = assetList;
    }

    @Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View itemLayoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_asset, null);
            return new DetailViewHolder(itemLayoutView);
    }

    @Override
    public void onBindViewHolder(BaseViewHolder holder, int position) {
        Asset asset = assetList.get(position);
        Last last = asset.getLast();
        if (holder.getItemViewType() == TYPE_DETAIL) {
            DetailViewHolder mHolder = (DetailViewHolder) holder;
            mHolder.dateTextView.setText(last.getDate());
            mHolder.brandTextView.setText(asset.getMc());
        }
    }

     class DetailViewHolder extends BaseViewHolder {

        @Bind(R.id.brandTextV)
        TextView brandTextView;
        @Bind(R.id.dateTextV)
        TextView dateTextView;

         DetailViewHolder(View itemLayoutView) {
            super(itemLayoutView);
            ButterKnife.bind(this, itemLayoutView);
        }
    }

}

回答1:


You do not need to call notifyDataSetChanged, it's an expensive operation your whole RecyclerView will completely redraw, rebind etc.

As the doc says:

This event does not specify what about the data set has changed, forcing any observers to assume that all existing items and structure may no longer be valid. LayoutManagers will be forced to fully rebind and relayout all visible views.

All you need to do is loop through every position and if needed update desired item otherwise do nothing or skip.

What you should do:

As you are updating your whole view first you need to compare your (visible) adapter's List<Asset> with new List<Asset> and retrieve only those items which you need to be update, once you have the list loop through updated list and update your adapter's view by using viewAdapter.notifyItemChanged(position).




回答2:


Perform the action to update the adapter as:

public void refreshAssetList(List<Asset> newAssetList){
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                recyclerViewAdapter.setAssetList(newAssetList);
                recyclerViewAdapter.notifyDataSetChanged();
            }
        }); 
}



回答3:


After some research I found DiffUtil class for updating list.

From the documentation :

DiffUtil is a utility class that can calculate the difference between two lists and output a list of update operations that converts the first list into the second one.

DiffUtil needs new list and old list. It only updates changing items in list. I created AssetDiffUtil class as below :

public class AssetDiffUtil extends DiffUtil.Callback {

    private final List<Asset> oldList;
    private final List<Asset> newList;

    public AssetDiffUtil(List<Asset> oldList, List<Asset> newList) {
        this.oldList = oldList;
        this.newList = newList;
    }

    @Override
    public int getOldListSize() {
        return oldList.size();
    }

    @Override
    public int getNewListSize() {
        return newList.size();
    }

    @Override
    public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
        return oldList.get(oldItemPosition).getId() == newList.get(newItemPosition).getId();
    }

    @Override
    public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
        final Last oldItem = oldList.get(oldItemPosition).getLast();
        final Last newItem = newList.get(newItemPosition).getLast();
        return oldItem.getDate().equals(newItem.getDate());
    }

    @Nullable
    @Override
    public Object getChangePayload(int oldItemPosition, int newItemPosition) {
        // Implement method if you're going to use ItemAnimator
        return super.getChangePayload(oldItemPosition, newItemPosition);
    }
}

Then I added swapItems method for refreshing list in AssetListRecyclerViewAdapter class.

  public void swapItems(List<Asset> newAssetList) {
        final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new AssetDiffUtil(this.assetList, newAssetList));
        this.assetList.clear();
        this.assetList.addAll(newAssetList);
        diffResult.dispatchUpdatesTo(this);
    }

That's it. But my problem still exist. Before using DiffUtil freezing time 4 seconds . Now, after using DiffUtil freezing time is 2 seconds. Unfortunately this is not a definitive solution for my problem.



来源:https://stackoverflow.com/questions/43608590/recyclerview-blocking-ui-thread-during-updates

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!