问题
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