Как исправить неправильную позицию, которую я получаю при отправке обновления ListUpdateCallback с помощью DiffUtil - PullRequest
1 голос
/ 19 июня 2019

У меня есть приложение Android, в котором пользователь может изменять несколько String элементов, используя EditText одновременно, поэтому мне нужно выяснить, что изменилось, чтобы уведомить сервер об изменениях (создать новый элемент, обновить существующий элемент или удалить последний), поэтому я использую DiffUtil с ListUpdateCallback для этого. Проблема в том, что если старый список имеет размер 2 элемента; и я удалил элемент с индексом 0, затем добавил 3 элемента в конец списка, я получаю обратный вызов к onInserted с неправильным параметром позиции, который приводит к IndexOutOfBoundsException, (и это поведение удаления любой элемент в старом списке, кроме последнего) , пожалуйста, посмотрите на этот GIF, который показывает проблему .

Я пробовал следующий код с другими изменениями, внесенными в новый список, такими как удаление по индексу 1, а затем добавление 3 элементов в конец списка, и все работает отлично!

Массив, с которым я работаю, имеет тип Answer, который является классом:

public class Answer {
    private String id;
    private String questionId;
    private String text;
    private Integer count;
}

Обратите внимание, что 2 разных объекта могут быть идентифицированы по идентификатору, если два элемента одинаковы; тогда содержимое может быть идентифицировано по тексту.

DiffUtil.Callback

public class AnswersDiffCallback extends DiffUtil.Callback {

    List<Answer> newAnswers;
    List<Answer> oldAnswers;

    public AnswersDiffCallback(List<Answer> newAnswers, List<Answer> oldAnswers) {
        this.newAnswers = newAnswers;
        this.oldAnswers = oldAnswers;
    }

    @Override
    public int getOldListSize() {
        return oldAnswers == null ? 0 : oldAnswers.size();
    }

    @Override
    public int getNewListSize() {
        return newAnswers == null ? 0 : newAnswers.size();
    }

    @Override
    public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
        Answer oldAnswer = oldAnswers.get(oldItemPosition);
        Answer newAnswer = newAnswers.get(newItemPosition);
        return Objects.equals(oldAnswer.getId(), newAnswer.getId());
    }

    @Override
    public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
        Answer oldAnswer = oldAnswers.get(oldItemPosition);
        Answer newAnswer = newAnswers.get(newItemPosition);
        return Objects.equals(oldAnswer.getText(), newAnswer.getText());
    }
}

в ListUpdateCallback Я пытаюсь зарегистрировать обратные вызовы, которые я получаю, чтобы проверить, работает ли он, прежде чем я говорю с сервером.

Log.d(TAG, "answers: oldAnswers = " + Utils.serializeObject(oldAnswers));
Log.d(TAG, "answers: newAnswers = " + Utils.serializeObject(newAnswers));
Log.d(TAG, "-----------------------------------------------------------------------------");
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new AnswersDiffCallback(newAnswers, oldAnswers), true);
diffResult.dispatchUpdatesTo(new ListUpdateCallback() {
    @Override
    public void onInserted(int position, int count) {
        try {
            Log.d(TAG, String.format("onInserted: (position, count) = (%d, %d)", position, count));
            for (int i = position; i < position + count; i++) {
                Log.d(TAG, "onInserted: newAnswer.text = " + newAnswers.get(i).getText());
            }
        } catch (Exception ex) {
            Log.e(TAG, "onInserted: Exception", ex);
        }
        Log.d(TAG, "-----------------------------------------------------------------------------");
    }

    @Override
    public void onRemoved(int position, int count) {
        try {
            Log.d(TAG, "onRemoved: (position, count) = (" + position + ", " + count + ")");
            for (int i = position; i < position + count; i++) {
                Log.d(TAG, String.format("onRemoved: (oldAnswer.id, oldAnswer.text) = (%s, %s)", oldAnswers.get(i).getId(), oldAnswers.get(i).getText()));
            }
        } catch (Exception ex) {
            Log.e(TAG, "onRemoved: Exception", ex);
        }
        Log.d(TAG, "-----------------------------------------------------------------------------");
    }

    @Override
    public void onMoved(int fromPosition, int toPosition) {
        try {
            Log.d(TAG, "onMoved: (fromPosition, toPosition) = (" + fromPosition + ", " + toPosition + ")");
            Log.d(TAG, String.format("onMoved: (oldAnswer.id, oldAnswer.text) = (%s, %s)", oldAnswers.get(fromPosition).getId(), oldAnswers.get(fromPosition).getText()));
            Log.d(TAG, String.format("onMoved: (newAnswer.id, newAnswer.text) = (%s, %s)", newAnswers.get(toPosition).getId(), newAnswers.get(toPosition).getText()));
        } catch (Exception ex) {
            Log.e(TAG, "onMoved: Exception", ex);
        }
        Log.d(TAG, "-----------------------------------------------------------------------------");
    }

    @Override
    public void onChanged(int position, int count, @Nullable Object payload) {
        try {
            Log.d(TAG, "onChanged: (position, count) = (" + position + ", " + count + ")");
            for (int i = position; i < position + count; i++) {
                Log.d(TAG, String.format("onChanged: (oldAnswer.id, oldAnswer.text) = (%s, %s)", oldAnswers.get(i).getId(), oldAnswers.get(i).getText()));
                Log.d(TAG, String.format("onChanged: (newAnswer.id, newAnswer.text) = (%s, %s)", newAnswers.get(i).getId(), newAnswers.get(i).getText()));
            }
        } catch (Exception ex) {
            Log.e(TAG, "onChanged: Exception", ex);
        }
        Log.d(TAG, "-----------------------------------------------------------------------------");
    }
});

и вот logcat за исключением того, что я получил:

2019-06-19 16:32:00.461 24515-24515/com.example.myApp D/AdminQuestionsFragment: answers: oldAnswers = [{"count":0,"id":"5d09a1236969e249cca42e96","questionId":"5d09a1236969e249cca42e95","text":"old answer 0"},{"count":0,"id":"5d09a1236969e249cca42e97","questionId":"5d09a1236969e249cca42e95","text":"old answer 1"}]
2019-06-19 16:32:00.501 24515-24515/com.example.myApp D/AdminQuestionsFragment: answers: newAnswers = [{"count":0,"id":"5d09a1236969e249cca42e97","questionId":"5d09a1236969e249cca42e95","text":"old answer 1"},{"text":"new answer 0"},{"text":"new answer 1"},{"text":"new answer 2"}]
2019-06-19 16:32:00.501 24515-24515/com.example.myApp D/AdminQuestionsFragment: -----------------------------------------------------------------------------
2019-06-19 16:35:19.550 24515-24515/com.example.myApp D/AdminQuestionsFragment: onInserted: (position, count) = (2, 3)
2019-06-19 16:35:19.550 24515-24515/com.example.myApp D/AdminQuestionsFragment: onInserted: newAnswer.text = new answer 1
2019-06-19 16:35:19.550 24515-24515/com.example.myApp D/AdminQuestionsFragment: onInserted: newAnswer.text = new answer 2
2019-06-19 16:35:19.599 24515-24515/com.example.myApp E/AdminQuestionsFragment: onInserted: Exception
    java.lang.IndexOutOfBoundsException: Index: 4, Size: 4
        at java.util.ArrayList.get(ArrayList.java:437)
        at com.example.myApp.views.AdminQuestionsFragment$5.onInserted(AdminQuestionsFragment.java:333)
        at androidx.recyclerview.widget.BatchingListUpdateCallback.dispatchLastEvent(BatchingListUpdateCallback.java:61)
        at androidx.recyclerview.widget.BatchingListUpdateCallback.onRemoved(BatchingListUpdateCallback.java:96)
        at androidx.recyclerview.widget.DiffUtil$DiffResult.dispatchRemovals(DiffUtil.java:921)
        at androidx.recyclerview.widget.DiffUtil$DiffResult.dispatchUpdatesTo(DiffUtil.java:836)
        at com.example.myApp.views.AdminQuestionsFragment.lambda$onActivityResult$8$AdminQuestionsFragment(AdminQuestionsFragment.java:320)
        at com.example.myApp.views.-$$Lambda$AdminQuestionsFragment$KZmQo8gdnjCYX1JsaACEVkjSd1s.onChanged(Unknown Source:8)
        at androidx.lifecycle.LiveData.considerNotify(LiveData.java:113)
        at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:126)
        at androidx.lifecycle.LiveData$ObserverWrapper.activeStateChanged(LiveData.java:424)
        at androidx.lifecycle.LiveData$LifecycleBoundObserver.onStateChanged(LiveData.java:376)
        at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:361)
        at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.java:188)
        at androidx.lifecycle.LiveData.observe(LiveData.java:185)
        at com.example.myApp.views.AdminQuestionsFragment.onActivityResult(AdminQuestionsFragment.java:387)
        at androidx.fragment.app.FragmentActivity.onActivityResult(FragmentActivity.java:170)
        at android.app.Activity.dispatchActivityResult(Activity.java:7454)
        at android.app.ActivityThread.deliverResults(ActivityThread.java:4353)
        at android.app.ActivityThread.handleSendResult(ActivityThread.java:4402)
        at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:49)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
2019-06-19 16:35:19.599 24515-24515/com.example.myApp D/AdminQuestionsFragment: -----------------------------------------------------------------------------
2019-06-19 16:35:19.600 24515-24515/com.example.myApp D/AdminQuestionsFragment: onRemoved: (position, count) = (0, 1)
2019-06-19 16:35:19.603 24515-24515/com.example.myApp D/AdminQuestionsFragment: onRemoved: (oldAnswer.id, oldAnswer.text) = (5d09a1236969e249cca42e96, old answer 0)
2019-06-19 16:35:19.603 24515-24515/com.example.myApp D/AdminQuestionsFragment: -----------------------------------------------------------------------------

проблема с этой строкой:

2019-06-19 16:35:19.550 24515-24515/com.example.myApp D/AdminQuestionsFragment: onInserted: (position, count) = (2, 3)

почему позиция не 1?

Обновление 1 Вот logcat, если у меня есть старый список из 2 элементов, и я удалил элемент с индексом 1, а затем добавил 3 элемента в конец списка:

2019-06-19 18:25:24.368 28118-28118/com.example.myApp D/AdminQuestionsFragment: answers: oldAnswers = [{"count":0,"id":"5d09a1236969e249cca42e96","questionId":"5d09a1236969e249cca42e95","text":"old answer 0"},{"count":0,"id":"5d09a1236969e249cca42e97","questionId":"5d09a1236969e249cca42e95","text":"old answer 1"}]
2019-06-19 18:25:24.370 28118-28118/com.example.myApp D/AdminQuestionsFragment: answers: newAnswers = [{"count":0,"id":"5d09a1236969e249cca42e96","questionId":"5d09a1236969e249cca42e95","text":"old answer 0"},{"text":"new answer 0"},{"text":"new answer 1"},{"text":"new answer 2"}]
2019-06-19 18:25:24.370 28118-28118/com.example.myApp D/AdminQuestionsFragment: -----------------------------------------------------------------------------
2019-06-19 18:25:24.370 28118-28118/com.example.myApp D/AdminQuestionsFragment: onRemoved: (position, count) = (1, 1)
2019-06-19 18:25:24.371 28118-28118/com.example.myApp D/AdminQuestionsFragment: onRemoved: (oldAnswer.id, oldAnswer.text) = (5d09a1236969e249cca42e97, old answer 1)
2019-06-19 18:25:24.371 28118-28118/com.example.myApp D/AdminQuestionsFragment: -----------------------------------------------------------------------------
2019-06-19 18:25:24.372 28118-28118/com.example.myApp D/AdminQuestionsFragment: onInserted: (position, count) = (1, 3)
2019-06-19 18:25:24.372 28118-28118/com.example.myApp D/AdminQuestionsFragment: onInserted: newAnswer.text = new answer 0
2019-06-19 18:25:24.372 28118-28118/com.example.myApp D/AdminQuestionsFragment: onInserted: newAnswer.text = new answer 1
2019-06-19 18:25:24.372 28118-28118/com.example.myApp D/AdminQuestionsFragment: onInserted: newAnswer.text = new answer 2
2019-06-19 18:25:24.372 28118-28118/com.example.myApp D/AdminQuestionsFragment: -----------------------------------------------------------------------------

1 Ответ

0 голосов
/ 19 июня 2019
for (int i = position; i < position + count; i++) {
    Log.d(TAG, "onInserted: newAnswer.text = " + newAnswers.get(i).getText());
}

Этот цикл определенно выдаст IndexOutOfBoundError. count - количество добавленных элементов. и position - позиция нового элемента. Когда вы делаете position + count, это определенно будет больше, чем размер вашего списка в какой-то момент. И, следовательно, ошибка.

Проблема в том, что если старый список имеет размер 2 элемента; и я удалил элемент с индексом 0, затем добавил 3 элемента в конец списка

Вы звоните dispatchUpdates после удаления и до добавления новых предметов?

Я думаю, что сначала вычисляются элементы, которые вставляются, а затем элемент, который удаляется, и, следовательно, позиция равна 2 в onInserted.

Вы можете обернуть ListUpdateCallback в BatchingListUpdateCallback, если хотите получать пакетные обновления.

...