2

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.

martinseal1987
  • 1,862
  • 8
  • 44
  • 77

0 Answers0