Using DiffUtil, How to prevent blocking the main thread when updating the recyclerView contained in a NestedScrollView?

久未见 提交于 2020-12-07 10:56:01

问题


I'm trying to udate my recyclerView in background Thread without any succes.

Updating the recyclerView still block the main thread.

Here my adapter:

public class PostAdapter extends RecyclerView.Adapter<PostAdapter.PostViewHolder> {
    
        private List<PostItems> postList;
        private Context context;
        private Queue<List<PostItems>> pendingUpdates = new ArrayDeque<>();
    
        PostAdapter(List<PostItems> postList, Context context) {
            this.postList = postList;
            this.context = context;
        }
    
        @NonNull
        @Override
        public PostViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_view_wall_post, parent, false);
            return new PostViewHolder(v);
        }
    
        @Override
        public void onBindViewHolder(@NonNull PostViewHolder holder, int position) {
            PostItems post_items = postList.get(position);
                 ...
                 ...
                 .........
                 ...
                 ...
        }
    
        @Override
        public int getItemCount() {
            return postList.size();
        }

        @Override
        public int getItemViewType(int position) {
            return position;
        }

        static class PostViewHolder extends RecyclerView.ViewHolder {
            PostViewHolder(@NonNull View itemView) {
                super(itemView);
                ...
                ...
                .........
                ...
                ...
            }
        }
    }


/*
* here the background Thread
* 
*/

// The Fragment or Activity will call this method
    // when new data becomes available
    public void updateItems(final List<PostItems> newItems) {
        pendingUpdates.add(newItems);
        if (pendingUpdates.size() > 1) {
            return;
        }
        updateItemsInternal(newItems);
    }
    // This method does the heavy lifting of
    // pushing the work to the background thread
    void updateItemsInternal(final List<PostItems> newItems) {
        final List<PostItems> oldItems = new ArrayList<>(this.postList);
        final Handler handler = new Handler();
        new Thread(new Runnable() {
            @Override
            public void run() {
                final DiffUtil.DiffResult diffResult =
                        DiffUtil.calculateDiff(new DiffCb(oldItems, newItems));
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        applyDiffResult(newItems, diffResult);
                    }
                });
            }
        }).start();
    }
    // This method is called when the background work is done
    protected void applyDiffResult(List<PostItems> newItems,
                                   DiffUtil.DiffResult diffResult) {
        pendingUpdates.remove();
        dispatchUpdates(newItems, diffResult);
        if (pendingUpdates.size() > 0) {
            updateItemsInternal(pendingUpdates.peek());
        }
    }
    // This method does the work of actually updating
    // the backing data and notifying the adapter
    protected void dispatchUpdates(List<PostItems> newItems,
                                   DiffUtil.DiffResult diffResult) {
        diffResult.dispatchUpdatesTo(this);
        postList.clear();
        postList.addAll(newItems);
        Toast.makeText(context, "b"+postList.size(), Toast.LENGTH_SHORT).show();
    }

Here is my DiffUtil class:

class DiffCb extends DiffUtil.Callback {
    private final List<PostItems> oldItems;
    private final List<PostItems> newItems;

    public DiffCb(List<PostItems> oldItems, List<PostItems> newItems) {
        this.oldItems = oldItems;
        this.newItems = newItems;
    }

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

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

    }

    @Override
    public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
        return oldItems.get(oldItemPosition).equals(newItems.get(newItemPosition));
    }

    @Override
    public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
        return oldItems.get(oldItemPosition).equals(newItems.get(newItemPosition));
    }
}

Updating recyclerView like that:

postList.addAll(response.body());
postAdapter.updateItems(homePostList);

Everything work fine when my recyclerView is not contained in a NestedScrollView or ScrollView.

But when my recyclerView is contained in a NestedScrollView or ScrollView, updating the recyclerView keep blocking the main thread as if the DiffUtil were disabled.

How to prevent blocking the main thread when updating the recyclerView contained in a NestedScrollView or ScrollView ?

Thanks.


回答1:


Try to not create everytime new adapter, but instead update data inside adapter. Also if you use DiffUtils, you should override .equals and .hashcode in your PostItems class so DIffUtils can know which object is new and which object already exists.

Click right mouse in somewhere your PostItems class and choose Generate, then choose override equals and hashcode, then choose unique field in class.

Also you should bind views in your ViewHolder class
For example:

       static class PostViewHolder extends RecyclerView.ViewHolder {
            TextView tv1;
            TextView tv2;
            PostViewHolder(@NonNull View itemView) {
                super(itemView);
                tv1 = itemView.findViewById(R.id.tv1);
                tv2 = itemView.findViewById(R.id.tv2);
            }
        }

Then in .bindViewHolder method you access your view like:

    @Override
    public void onBindViewHolder(@NonNull PostViewHolder holder, int position) {
        PostItems post_items = postList.get(position);
        holder.tv1.setText(post_items.get_text_wall_post());
        //etc
    }

Main idea is to not call everytime .findViewById() in .bindViewHolder method, because it's slow enaugh for RecyclerView



来源:https://stackoverflow.com/questions/64196657/using-diffutil-how-to-prevent-blocking-the-main-thread-when-updating-the-recycl

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