Search code examples
androidandroid-support-libraryandroid-recyclerview

NullPointerException - Attempt to invoke virtual method RecyclerView$ViewHolder.shouldIgnore()' on a null object reference


Several developers have reported seeing the following stack trace since upgrading to Android Support 23.2.0:

java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.support.v7.widget.RecyclerView$ViewHolder.shouldIgnore()' on a null object reference
    at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2913)
    at android.support.v7.widget.RecyclerView.consumePendingUpdateOperations(RecyclerView.java:1445)
    at android.support.v7.widget.RecyclerView.access$400(RecyclerView.java:144)
    at android.support.v7.widget.RecyclerView$1.run(RecyclerView.java:282)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:821)
    at android.view.Choreographer.doCallbacks(Choreographer.java:606)
    at android.view.Choreographer.doFrame(Choreographer.java:575)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:807)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:145)
    at android.app.ActivityThread.main(ActivityThread.java:6895)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)

This occurs when RecyclerView's change animation is enabled and corresponding RecyclerView.Adapter methods notifyItemInserted(), notifyItemRemoved(), etc., are called to indicate that an isolated change was made to the list managed by the adapter (as opposed to a wholesale change, as indicated by notifyDataSetChanged()).

Is this due to a bug in RecyclerView, or are we developers doing something wrong?


Solution

  • This appears to be due to a bug in RecyclerView, which was introduced in 23.2.0. The bug was reported here, and I explained what I think is causing the error in comment #5 on that bug.

    Here's my explanation, copied here for historical purposes and ease of reference:

    I found the source of this problem. Within RecyclerView.dispatchLayoutStep3(), there's a for loop, "for (int i = 0; i < count; ++i)", where count is based on mChildHelper.getChildCount(). While this iteration is occurring, the collection managed by ChildHelper is modified by ChildHelper.hideViewInternal(), which results in null being returned from the call to mChildHelper.getChildAt() on line 3050 of RecyclerView, which in turn results in null being returned from getChildViewHolderInt() on the same line of code (RecyclerView:3050).

    Here's the chain of method calls that results in the modification that breaks the integrity of the for loop:

    dispatchLayoutStep3() -> animateChange() -> addAnimatingView() -> hide() -> hideViewInternal()

    When ChildHelper adds the child param to its mHiddenViews collection, it violates the integrity of the for loop way up in dispatchLayoutStep3().

    I see two workarounds for this:

    1) Disable change animation in your RecyclerView

    2) Downgrade to 23.1.1, where this wasn't a problem