RecyclerView blocking ui thread during updates

2019-07-23 01:21发布

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);
        }
    }

}

3条回答
我欲成王,谁敢阻挡
2楼-- · 2019-07-23 01:55

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楼-- · 2019-07-23 02:00

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.

查看更多
The star\"
4楼-- · 2019-07-23 02:09

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).

查看更多
登录 后发表回答