-
Notifications
You must be signed in to change notification settings - Fork 727
Description
Epoxy Version - 3.11.0
We've noticed a handful of ClassCastException
s occurring. Over the course of a day with at least a thousand visitors to a screen, we're seeing one, maybe two crashes on our fragment. The crashes all look like the following stacktrace.
Fatal Exception: java.lang.ClassCastException
com.x.y.z.ItemDivider_.handlePreBind (ItemDivider_.java:22)
com.airbnb.epoxy.EpoxyViewHolder.bind (EpoxyViewHolder.java:53)
com.airbnb.epoxy.BaseEpoxyAdapter.onBindViewHolder (BaseEpoxyAdapter.java:104)
com.airbnb.epoxy.BaseEpoxyAdapter.onBindViewHolder (BaseEpoxyAdapter.java:19)
androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder (RecyclerView.java:7107)
androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline (RecyclerView.java:6012)
androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline (RecyclerView.java:6279)
androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition (RecyclerView.java:6118)
androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition (RecyclerView.java:6114)
androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next (LinearLayoutManager.java:2303)
androidx.recyclerview.widget.LinearLayoutManager.layoutChunk (LinearLayoutManager.java:1627)
androidx.recyclerview.widget.LinearLayoutManager.fill (LinearLayoutManager.java:1587)
androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren (LinearLayoutManager.java:665)
androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2 (RecyclerView.java:4134)
androidx.recyclerview.widget.RecyclerView.onMeasure (RecyclerView.java:3540)
The EpoxyModel in question
@EpoxyModelClass(layout = R.layout.view_divider)
abstract class ItemDivider : EpoxyModelWithHolder<ItemDivider.DividerHolder>() {
class DividerHolder : KotlinEpoxyHolder()
}
Its layout
<?xml version="1.0" encoding="utf-8"?>
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_1"
android:background="@drawable/linear_layout_divider_vertical" />
The crashes are happening so far are occurring on four different views on the same screen. The other four views also vary in their complexity (non-empty holders, layouts containing a variety of child elements, etc). The only commonality between the views that causing these fatal exceptions is that they are all conditionally shown in the EpoxyRecyclerView
.
For example, the following is a trimmed down version of what our code looks like. The state parameter to the function in this case is a data class contained a variety of fields which are the substates of the screen which get rendered out. The state comes from an rx stream in a ViewModel
, and the substates all result from various network calls which update and emit a new state, with one relevant piece of the total state changed. Some substates are data classes, others are sealed classes. The ClassCastException
s indicate that Models which are being conditionally shown, like the ItemDivider
model, is trying to be cast to a view that immediately follows / precedes it in the list.
private fun buildLayout(state: MyScreenViewState) {
binding.epoxyRecyclerView.withModels {
if (state.showProgress) {
progressView { id("PROGRESS_INDICATOR") }
} else {
when (state.userState) {
UserState.Hidden, is UserState.Visible -> {
welcomeHeaderView {
id("WELCOME_HEADER")
userState(state.userState)
}
}
UserState.Anonymous -> {
signInView {
id("SIGN_IN_VIEW")
}
}
}
if (state.BannerState is BannerState.Visible) {
announcementBannerView {
id("ANNOUNCEMENT_BANNER")
viewState(state.BannerState)
}
if (state.userDetailState !is UserDetailState.Hidden) {
itemDivider { id("DETAIL_TOP_DIVIDER") }
}
if (state.userState is UserState.Visible) {
userView {
id("USER_VIEW")
userState(state.userState)
}
leftInsetDivider { id("DETAIL_BOTTOM_DIVIDER") }
}
spacer { id("SPACER_TWO") }
if (state.userState is UserState.Visible) {
paymentDetailsView {
id("PAYMENT_DETAILS")
}
spacer { id("SPACER_FOUR") }
}
}
}
}
One final thing we've noticed from logs on BugSnag, is that crashes tend to happen when the fragment and activity its in pause & stop or start & resume. Also, I've been unable to reproduce these crashes locally when following user breadcrumbs to replicate what they were doing when the crash occurred. We've used epoxy extensively within our app without any similar crashes. Any thoughts to what could be going wrong here?