Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RecyclerView gets crashed [IllegalArgumentException] when use notifyItemChanged from Handler using Runnable

I'm using RecyclerView as a list to show songs that can be downloaded. Each item has ProgressBar in its View. When the download starts, then I use a Handler to notify each item to update the ProgressBar to show the song download progress.

Q1. Is this is a correct way to do it or Is there any other way to do it more appropriately.

Q2. RecyclerView gets crashed when we use adapter.notifyItemChanged(position); to update the content of single item. It is called from a Handler using Runnable. But, the log don't show any traces for my code. Why?

Below is the log for this issue:

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)

I searched to find the solution for this but couldn't find any appropriate answer.

like image 918
Amrut Bidri Avatar asked May 06 '15 13:05

Amrut Bidri


4 Answers

We also had this issue on our application, and after a really long debugging session we found out that it was caused by adapter.setHasStableIds(true)

We removed the offending line and the issue is finally gone.

Hope it helps.

like image 139
Henrique de Sousa Avatar answered Nov 11 '22 13:11

Henrique de Sousa


i had the same issue,i could handle it by disabling the animator of recyclerview:

recyclerView.itemAnimator = null

like image 39
ali samawi Avatar answered Nov 11 '22 14:11

ali samawi


For me the issue was resolved after I removed animateLayoutChanges=“true” from the parent ViewGroup of the recyclerview.

like image 12
Levon Petrosyan Avatar answered Nov 11 '22 13:11

Levon Petrosyan


Some people here say that "android:animateLayoutChanges" should not be set to true, but I didn't set it, and setting it to false didn't help either.

Anyway, for some reason, in my case, replacing ConstraintLayout with LinearLayout as the parent of the RecyclerView has fixed it.

Another alternative is that before I let the adapter of the RecyclerView have any changes on it or have any changes on the RecyclerView's parent (in my case I switch between views in ViewAnimator), I added this check:

                   if (recyclerView.isAnimating)
                        Handler().post {
                            adapter.items = it.listItems
                            adapter.notifyDataSetChanged()
                        }
                    else {
                         adapter.items = it.listItems
                         adapter.notifyDataSetChanged()
                         }

For some reason this alternative didn't work for me on POC, so I suggest the LinearLayout solution instead, or use this in case you clear the data or switch to another view like I do:

                val itemAnimator = recyclerView.itemAnimator
                recyclerView.itemAnimator = null
                clearItems()
                recyclerView.itemAnimator = itemAnimator
    fun clearItems() {
        items.clear()
        recyclerView.adapter!!.notifyDataSetChanged()
        viewSwitcher.setViewToSwitchTo(R.id.emptyView)
    }

I reported about this issue to Google here. The POC and the workarounds (that work for me) are available there too.

The POC shows that this occurs if the RecyclerView is inside ConstraintLayout, which is inside ViewAnimator. The scenario is that while the RecyclerView is animating some of its views (removing a single item, for example), I clear all of its items and switch to another view inside the ViewAnimator.

like image 6
android developer Avatar answered Nov 11 '22 14:11

android developer