RecyclerView получает сбой [IllegalArgumentException] при использовании notifyItemChanged из обработчика с помощью Runnable

Я использую RecyclerView в качестве списка, чтобы показывать песни, которые можно загрузить. Каждый элемент имеет ProgressBar в своем View. Когда начинается загрузка, я использую Handler для уведомления каждого элемента, чтобы обновить ProgressBar, чтобы показать ход загрузки песни.

Q1. Это правильный способ сделать это или есть ли другой способ сделать это более подходящим образом.

Q2. RecyclerView разбивается, когда мы используем adapter.notifyItemChanged(position); для обновления содержимого отдельного элемента. Он вызывается из Handler с помощью Runnable. Но в журнале не отображаются следы для моего кода. Почему?

Ниже приведен журнал для этой проблемы:

05-06 19:09:45.804: E/AndroidRuntime(32115): FATAL EXCEPTION: main
05-06 19:09:45.804: E/AndroidRuntime(32115): java.lang.IllegalArgumentException: Tmp detached view should be removed from RecyclerView before it can be recycled: ViewHolder{41b7bec0 position=6 id=-1, oldPos=-1, pLpos:-1 update changed tmpDetachedundefined adapter position no parent}
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.support.v7.widget.RecyclerView$Recycler.recycleViewHolderInternal(RecyclerView.java:3861)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.support.v7.widget.RecyclerView.removeAnimatingView(RecyclerView.java:779)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.support.v7.widget.RecyclerView.access$5300(RecyclerView.java:127)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.support.v7.widget.RecyclerView$ItemAnimatorRestoreListener.onAddFinished(RecyclerView.java:8228)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.support.v7.widget.RecyclerView$ItemAnimator.dispatchAddFinished(RecyclerView.java:8573)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.support.v7.widget.DefaultItemAnimator$5.onAnimationEnd(DefaultItemAnimator.java:239)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.support.v4.view.ViewPropertyAnimatorCompatJB$1.onAnimationEnd(ViewPropertyAnimatorCompatJB.java:47)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.view.ViewPropertyAnimator$AnimatorEventListener.onAnimationEnd(ViewPropertyAnimator.java:973)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.animation.ValueAnimator.endAnimation(ValueAnimator.java:1012)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.animation.ValueAnimator.access$400(ValueAnimator.java:51)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.animation.ValueAnimator$AnimationHandler.doAnimationFrame(ValueAnimator.java:623)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.animation.ValueAnimator$AnimationHandler.run(ValueAnimator.java:639)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:776)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.view.Choreographer.doCallbacks(Choreographer.java:579)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.view.Choreographer.doFrame(Choreographer.java:547)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:762)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.os.Handler.handleCallback(Handler.java:725)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.os.Handler.dispatchMessage(Handler.java:92)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.os.Looper.loop(Looper.java:153)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at android.app.ActivityThread.main(ActivityThread.java:5297)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at java.lang.reflect.Method.invokeNative(Native Method)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at java.lang.reflect.Method.invoke(Method.java:511)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:833)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
05-06 19:09:45.804: E/AndroidRuntime(32115):    at dalvik.system.NativeStart.main(Native Method)

Я искал, чтобы найти решение для этого, но не нашел подходящего ответа.

Ответ 1

У нас также была эта проблема в нашем приложении, и после очень долгого отладочного сеанса мы выяснили, что это было вызвано adapter.setHasStableIds(true)

Мы удалили строку нарушения, и проблема наконец исчезла.

Надеюсь, что это поможет.

Ответ 2

adapter.notifyItemChanged(position); должен быть вызван из основного потока

вместо вашего обработчика используйте Handler с mainLooper

    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            //HERE
        }
    });

Ответ 3

Существует несколько способов обхода проблемы:

  • Вызовите notifyDataSetChanged() вместо notifyItemChanged(). Это менее эффективный способ его решения.

  • Как указано Henrique de Sousa, удалите строку adapter.setHasStableIds(true), чтобы предотвратить проблему.

Но реальное решение (как прокомментировано ютертон), если адаптер устойчив, заключается в том, чтобы реализовать и правильно getItemId(), соблюдая установленное значение hasStableIds.