airbnb / epoxy

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

Carousel - Handler sending message to a Handler on a dead thread #1123

Closed anscash closed 3 years ago

anscash commented 3 years ago

Hey there,

sometimes while switching through my fragment hierarchy, i recognize the carousels are not loading their items. When this happens, i can see this exception in the Log:

2021-01-25 14:25:42.218 21755-21755/com.xxxx.xxxxx W/MessageQueue: Handler (android.os.Handler) {7dae8a3} sending message to a Handler on a dead thread
    java.lang.IllegalStateException: Handler (android.os.Handler) {7dae8a3} sending message to a Handler on a dead thread
        at android.os.MessageQueue.enqueueMessage(MessageQueue.java:618)
        at android.os.Handler.enqueueMessage(Handler.java:759)
        at android.os.Handler.sendMessageAtTime(Handler.java:708)
        at android.os.Handler.sendMessageDelayed(Handler.java:678)
        at android.os.Handler.postDelayed(Handler.java:479)
        at com.airbnb.epoxy.EpoxyController.requestDelayedModelBuild(EpoxyController.java:233)
        at com.airbnb.epoxy.EpoxyController.requestModelBuild(EpoxyController.java:159)
        at com.airbnb.epoxy.SimpleEpoxyController.requestModelBuild(SimpleEpoxyController.java:30)
        at com.airbnb.epoxy.SimpleEpoxyController.setModels(SimpleEpoxyController.java:20)
        at com.airbnb.epoxy.EpoxyRecyclerView.setModels(EpoxyRecyclerView.kt:393)
        at com.airbnb.epoxy.Carousel.setModels(Carousel.java:497)
        at com.xxxxxxxx.xxxxxxx.epoxy.modelsx.xxxxxxxxxx_.bind(EpoxyModelFeedWidgetContainerModel_.java:180)
        at com.xxxxxxxx.xxxxxxx.epoxy.models.xxxxxxxxxxx_.bind(EpoxyModelFeedWidgetContainerModel_.java:31)
        at com.airbnb.epoxy.EpoxyViewHolder.bind(EpoxyViewHolder.java:58)
        at com.airbnb.epoxy.BaseEpoxyAdapter.onBindViewHolder(BaseEpoxyAdapter.java:98)
        at com.airbnb.epoxy.BaseEpoxyAdapter.onBindViewHolder(BaseEpoxyAdapter.java:15)
        at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7107)
        at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:6012)
        at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6279)
        at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6118)
        at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6114)
        at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303)
        at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1627)
        at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
        at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:665)
        at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4134)
        at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3851)
        at androidx.recyclerview.widget.RecyclerView.consumePendingUpdateOperations(RecyclerView.java:1886)
        at androidx.recyclerview.widget.RecyclerView$1.run(RecyclerView.java:414)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1057)
        at android.view.Choreographer.doCallbacks(Choreographer.java:875)
        at android.view.Choreographer.doFrame(Choreographer.java:772)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1042)
        at android.os.Handler.handleCallback(Handler.java:888)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:213)
        at android.app.ActivityThread.main(ActivityThread.java:8178)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1101)

With kind regards

elihart commented 3 years ago

What thread do you have epoxy configured to build models on? and why would that thread be dead? it seems like potentially a configuration issue

anscash commented 3 years ago

Hi @elihart thanks for your fast reply!

My fragment-structure is basically the following:

ON CREATE

HandlerThread handlerThread = new HandlerThread("ID");
handlerThread.start();
Handler myHandler = new Handler(handlerThread.getLooper());
.....

ON VIEW CREATED

MyEpoxyController epoxyController = new MyEpoxyController();       // extends EpoxyController
epoxyController.defaultDiffingHandler = myHandler ;
epoxyController.defaultModelBuildingHandler = myHandler ;
.....

ON DESTROY

handlerThread.quitSafely()
.....

With kind regards

elihart commented 3 years ago

well yes, you shouldn't be quitting your thread in onDestroy, that's why it doesn't exist...

the thread should be long lived across all fragments

elihart commented 3 years ago

defaultModelBuildingHandler and defaultDiffingHandler are static fields

anscash commented 3 years ago

Thank you very much, i will try.

By the way, your library is awesome!