airbnb / epoxy

Epoxy is an Android library for building complex screens in a RecyclerView
https://goo.gl/eIK82p
Apache License 2.0
8.46k stars 730 forks source link

EpoxyViewHolder's epoxyModel becames null for sticky epoxyModel #1372

Open turkergoksu opened 5 months ago

turkergoksu commented 5 months ago

So in Crashlytics we got a crash like the one below.

Fatal Exception: java.lang.IllegalStateException: This holder is not currently bound.
       at com.airbnb.epoxy.EpoxyViewHolder.assertBound(EpoxyViewHolder.java:135)
       at com.airbnb.epoxy.EpoxyViewHolder.getHolder(EpoxyViewHolder.java:129)
       at com.airbnb.epoxy.EpoxyVisibilityTracker.processChild(EpoxyVisibilityTracker.kt:218)
       at com.airbnb.epoxy.EpoxyVisibilityTracker.processChangeEventWithDetachedView(EpoxyVisibilityTracker.kt:179)
       at com.airbnb.epoxy.EpoxyVisibilityTracker.processChangeEvent(EpoxyVisibilityTracker.kt:158)
       at com.airbnb.epoxy.EpoxyVisibilityTracker.itemAnimatorFinishedListener$lambda$0(EpoxyVisibilityTracker.kt:40)
       at androidx.recyclerview.widget.RecyclerView$ItemAnimator.isRunning(RecyclerView.java:14231)
       at com.airbnb.epoxy.EpoxyVisibilityTracker.processChangeEvent(EpoxyVisibilityTracker.kt:153)
       at com.airbnb.epoxy.EpoxyVisibilityTracker.processChangeEvent$default(EpoxyVisibilityTracker.kt:143)
       at com.airbnb.epoxy.EpoxyVisibilityTracker$Listener.onScrolled(EpoxyVisibilityTracker.kt:384)
       at androidx.recyclerview.widget.RecyclerView.dispatchOnScrolled(RecyclerView.java:5688)
       at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep3(RecyclerView.java:4741)
       at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:4367)
       at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4919)

We couldn't reproduce the crash but we suspected that it's related to sticky epoxyModel. So we removed sticky configuration and saw that crashes went down.

I've decided to log boundedViewHolders(epoxyController.adapter.boundViewHolders) by printing inside scroll listener. Then I put an epoxyModel with a sticky on the first element of the RecyclerView and did a quick up and down like below image.

stickyissue

And in the logs I saw an EpoxyViewHolder with epoxyModel=null which is our sticky epoxyModel.

Screen Shot 2024-01-26 at 13 39 45

My general assumption is that Epoxy creates two ViewHolders for sticky epoxyModels and when I scroll down, non-sticky holder gets unbinded and sets epoxyModel to null. But when I scroll up again since it's removed before it can't find the epoxyModel.

I'm not sure but cause might be related to both non-sticky and sticky has same ids or same layout resources. Because when we try to create ViewHolder in BaseEpoxyAdapter we get model according to viewType which is equal to resource layout in my case.

@Override
  public EpoxyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    EpoxyModel<?> model = viewTypeManager.getModelForViewType(this, viewType);
    View view = model.buildView(parent);
    return new EpoxyViewHolder(parent, view, model.shouldSaveViewState());
  }

What do you think what might be the cause? Would it be possible to differentiate ids or viewtypes for sticky epoxyModels?