question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Occasional ClassCastExceptions occurring with conditionally shown views

See original GitHub issue

Epoxy Version - 3.11.0

We’ve noticed a handful of ClassCastExceptions 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 ClassCastExceptions 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?

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:1
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
christophernikschcommented, Sep 8, 2020

Thanks for the suggestions. I’ll delve into RecyclerView further to see if i can pinpoint anything. I’ve yet to reproduce this locally unfortunately. I have modified our screen to lift the visibility toggling logic out of the EpoxyModels and into the the Controller. I’ll post a followup when we update our app if that change fixes things or not.

0reactions
christophernikschcommented, Feb 21, 2021

I’m going to (finally) close this issue out, as it seems to be fixed now. While I was never able to reproduce this locally, it appears that the cause of the crash was from two of our EpoxyModels toggling their visibility. For some context, we had some ViewPagers that would show cards to our users, typically 1 to 3 cards at a time. Based on some logging I collected of the screen state at the time, it appeared that the state of the cards was changing right as the crash occurred. The ViewPagers are wrapped into their own components which manage the appearance of the ViewPager differently when there’s either a single card, or multiple. It appears that these card pagers were setting their root visibility to gone when all of the cards were dismissed by the user.

The solution I used (since we weren’t rebuilding these card pagers in epoxy) was to wrap their layout in a LinearLayout so that the EpoxtModel wasn’t technically being set to GONE and I also put in some better state handling code to prevent EpoxyModels from being added to the EpoxyRecyclerView if no cards were being shown. These two things combined, seem to have resolved this crash we were seeing.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Fixing ClassCastException - YouTube
A ClassCastException occurs when you attempt to cast an object to a variable type that does not represent it. In this video, we...
Read more >
Activity - Android Developers
The visible lifetime of an activity happens between a call to onStart() until ... to be shown for the given view (multiple views...
Read more >
Knowledge Base - Customer Support
Applies to Version 9 Observed Behavior Recently view product activity can display inconsistent results, sometimes not populating.
Read more >
iOS Storyboard Conditionally showing Views - Stack Overflow
The first ViewController in this example is actually a split view controller. Currently within the iPad app when a user clicks on the...
Read more >
Cognos Business Intelligence 10.2.1 Fix Lists - IBM
PM03210, "RQP-DEF-0103 CROSS JOIN" error on Query Studio occurs when ... PM04194, Hide/Show functionality for categories in PowerPlay web ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found