Skip to content

[epoxy-paging] Notify Executor crash in PagedListEpoxyController.submitList when used with MvRx #677

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
chrisbanes opened this issue Feb 5, 2019 · 8 comments

Comments

@chrisbanes
Copy link
Contributor

chrisbanes commented Feb 5, 2019

This is a follow on from #567 but specifically when used with MvRx.

I'm seeing sporadic: java.lang.IllegalArgumentException: The notify executor for your PagedList must use the same thread as the model building handler set in PagedListEpoxyController.modelBuildingHandler exceptions when used with MvRx.

I've checked the loopers in my Fragment's invalidate() and Looper.myLooper == Looper.getMainLooper. I'm using the default modelBuildingHandler so I don't think this is an obvious threading issue

My paging setup is here and my submit is here.

This might be something to do with the fact that MvRx is async all the time. I'll try and debug this more tomorrow, but figured I'd throw this up here in case anyone is using MvRx + Paging + Epoxy together.

@elihart
Copy link
Contributor

elihart commented Feb 5, 2019

Thanks for reporting - I don't see anything wrong with your setup at first glance.

Can you post a stacktrace? any debugging you could do on your side would be helpful too. How rare is this?

@chrisbanes
Copy link
Contributor Author

chrisbanes commented Feb 7, 2019

Here's a stacktrace:

java.lang.IllegalArgumentException: The notify executor for your PagedList must use the same thread as the model building handler set in PagedListEpoxyController.modelBuildingHandler
        at com.airbnb.epoxy.paging.PagedListModelCache.assertUpdateCallbacksAllowed(PagedListModelCache.kt:127)
        at com.airbnb.epoxy.paging.PagedListModelCache.access$assertUpdateCallbacksAllowed(PagedListModelCache.kt:51)
        at com.airbnb.epoxy.paging.PagedListModelCache$updateCallback$1.onRemoved(PagedListModelCache.kt:105)
        at androidx.recyclerview.widget.BatchingListUpdateCallback.dispatchLastEvent(BatchingListUpdateCallback.java:64)
        at androidx.recyclerview.widget.BatchingListUpdateCallback.onInserted(BatchingListUpdateCallback.java:82)
        at androidx.recyclerview.widget.DiffUtil$DiffResult.dispatchAdditions(DiffUtil.java:881)
        at androidx.recyclerview.widget.DiffUtil$DiffResult.dispatchUpdatesTo(DiffUtil.java:840)
        at androidx.paging.PagedStorageDiffHelper.dispatchDiff(PagedStorageDiffHelper.java:163)
        at androidx.paging.AsyncPagedListDiffer.latchPagedList(AsyncPagedListDiffer.java:335)
        at androidx.paging.AsyncPagedListDiffer$2$1.run(AsyncPagedListDiffer.java:312)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

@chrisbanes
Copy link
Contributor Author

Hmmm, debugged this more.

So it seems like the EpoxyController.defaultModelBuildingHandler.looper() != Looper.getMainLooper(), although I can't see how this happens from the source.

If I explicitly pass in a Handler which uses main looper, the crashes disappear.

class WatchedEpoxyController() : PagedListEpoxyController<WatchedShowEntryWithShow>(
        modelBuildingHandler = Handler(Looper.getMainLooper())
)

@elihart
Copy link
Contributor

elihart commented Feb 7, 2019

Thanks for digging into this, it is very strange that manually using Handler(Looper.getMainLooper()) fixes the crashes for you.

Possibly there is a bug in how the default main handler is set up, although the code does look pretty straightforward, I'll see what I can figure out.

I'm glad you have a workaround in the meantime though - once you manually set that handler you don't have any more issues?

chrisbanes pushed a commit to chrisbanes/tivi that referenced this issue Feb 8, 2019
@chrisbanes
Copy link
Contributor Author

chrisbanes commented Feb 8, 2019

Really weird things are happening here...

screenshot 2019-02-08 at 10 43 10

screenshot 2019-02-08 at 10 43 17

screenshot 2019-02-08 at 10 43 24

@elihart
Copy link
Contributor

elihart commented Feb 8, 2019

Wow, bizarre! That is very helpful though.

You wouldn't by any chance be setting EpoxyController#defaultModelBuildingHandler statically somewhere?

The model building looper you are showing is using a HandlerThread with the name epoxy. The only place that is built is in EpoxyAsyncUtil#getAsyncBackgroundHandler, and I don't know how it would be set as the model building looper if somewhere in your app wasn't changing that.

It is static and a global setting, so that would be an easy mistake to make.

@chrisbanes
Copy link
Contributor Author

chrisbanes commented Feb 9, 2019

Woops, turns out that I was manually setting defaultModelBuildingHandler 🤦‍♂️ . Thanks for the help!

@elihart
Copy link
Contributor

elihart commented Feb 10, 2019

Ah, glad you figured it out. Any thoughts on how to make it harder to make this mistake?

I think an easy change would be to print out the thread information of both threads when the same thread validation is done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants