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