I'm updating a RecyclerView using diff utils callback it seems to work fine except if I click a view in the adapter (this forces the views to essentially shuffle) super fast my app will crash with a IndexOutOfBoundsException: Index: 25, Size: 30, except its not out of bounds according to the logs, i think this is because I'm using the diff utils in an async task, but i thought this was how to do it, process in the background (as if the list is big diff utils is quite a lot of computation) and post to the adapter? any help would be appreciated, here is my
Visitable diff utils
public class VisitableDiffUtils extends DiffUtil.Callback {
private final List<Visitable> oldVisitable;
private final List<Visitable> newVisitable;
private final String TAG = "DIFFUTIL";
private final TypeFactory typeFactory;
public VisitableDiffUtils(List<Visitable> oldVisitable,
List<Visitable> newVisitable) {
this.typeFactory = new TypeFactoryForList();
this.oldVisitable = oldVisitable;
this.newVisitable = newVisitable;
}
@Override
public int getOldListSize() {
return oldVisitable != null ? oldVisitable.size() : 0;
}
@Override
public int getNewListSize() {
return newVisitable != null ? newVisitable.size() : 0;
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return Objects.equals(oldVisitable.get(oldItemPosition).id(),
newVisitable.get(newItemPosition).id());
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return oldVisitable
.get(oldItemPosition).toString().equals(newVisitable.get(newItemPosition).toString());
}
@Nullable
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
Log.d(TAG,"getChangePayload");
if (newVisitable.get(newItemPosition).type(typeFactory) == CardViewHolder.LAYOUT){
Sentence newSentence = (Sentence) newVisitable.get(newItemPosition);
Sentence oldSentence = (Sentence) oldVisitable.get(oldItemPosition);
Bundle diffBundle = new Bundle();
if (newSentence.getClicks() != oldSentence.getClicks()) {
Log.d(TAG,"putInt(Constants.CARD_CLICKED");
diffBundle.putInt(Constants.CARD_CLICKED, newSentence.getClicks());
}
if (diffBundle.size() == 0) return null;
return diffBundle;
} else {
GroupsWithSentences newSentence = (GroupsWithSentences) newVisitable.get(newItemPosition);
GroupsWithSentences oldSentence = (GroupsWithSentences) oldVisitable.get(oldItemPosition);
Bundle diffBundle = new Bundle();
if (newSentence.getGroupItems().size() != oldSentence.getGroupItems().size()) {
diffBundle.putInt(Constants.CARD_CLICKED, newSentence.getGroupItems().size());
}
if (diffBundle.size() == 0) return null;
return diffBundle;
}
}
}
Log from the crash
Caused by: java.lang.IndexOutOfBoundsException: Index: 25, Size: 30
at java.util.ArrayList.get(ArrayList.java:437)
at VisitableDiffUtils.areItemsTheSame(VisitableDiffUtils.java:47)
and the code in my adapter
I call refreshDiffUtilsList when I want to update it
class CalculateDiffUtils extends AsyncTask<Void, Void, DiffResult> {
private final List<Visitable> oldCardList;
private final List<Visitable> newCardList;
CalculateDiffUtils(List<Visitable> oldCardList, List<Visitable> newCardList) {
this.oldCardList = oldCardList;
this.newCardList = newCardList;
}
@Override
protected DiffUtil.DiffResult doInBackground(Void... params) {
return DiffUtil.calculateDiff(new VisitableDiffUtils(oldCardList, newCardList));
}
@Override
protected void onPostExecute(DiffUtil.DiffResult diffResult) {
super.onPostExecute(diffResult);
dispatchUpdates(diffResult, newCardList);
}
}
private void dispatchUpdates(DiffUtil.DiffResult diffResult, List<Visitable> newCardList) {
this.elements.clear();
this.elements.addAll(newCardList);
diffResult.dispatchUpdatesTo(this);
}
public void refreshDiffUtilsList(List<Visitable> sentences) {
new CalculateDiffUtils(elements, sentences).execute();
}
as mentioned this works fine if I just click one at a time in a nice leisurely manner but if I spam the button I get this crash.