maxkeppeler / sheets

⭐ ‎‎‎‏‏‎ ‎Offers a range of beautiful sheets (dialogs & bottom sheets) for quick use in your project. Includes many ways to customize sheets.
https://maxkeppeler.github.io/sheets/
Apache License 2.0
921 stars 77 forks source link

Crash when putting app into app switcher while sheet is open #50

Closed russellbanks closed 3 years ago

russellbanks commented 3 years ago

Describe the bug If you swipe the app into the app switcher while a sheet is open, when you return to it, the app will crash.

Library Version: 2.1.3

To Reproduce Steps to reproduce the behavior:

  1. Open a sheet
  2. Swipe app into the app switcher
  3. Return to the app

Expected behavior The user should be able to return to the app without it crashing and the sheet remain as it was.

Additional context This also occurs in the sheets sample app.

Screenshots I have attached a video of this happening in my app.

https://user-images.githubusercontent.com/74878137/110211291-e442be80-7e8d-11eb-9483-54ae8115a591.mp4

maxkeppeler commented 3 years ago

Can you show me the exception that is thrown? Does this happen with every type of sheet in the sample? (for dialog and bottom-sheet?)

russellbanks commented 3 years ago

I have done some testing on the sample app. It occurs regardless of dialog or bottom sheet but only seems to happen with specific use cases within the sample app. For example, it can work in some InfoSheets but not in others. It crashes all sheets in my app.

In the sample app,

This is the exception I get when it occurs in my app:


    Process: org.bandev.buddhaquotes, PID: 10722
    java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = org.bandev.buddhaquotes.fragments.QuoteFragment$onViewCreated$3$1$1)
        at android.os.Parcel.writeSerializable(Parcel.java:1833)
        at android.os.Parcel.writeValue(Parcel.java:1780)
        at android.os.Parcel.writeArrayMapInternal(Parcel.java:928)
        at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1589)
        at android.os.Bundle.writeToParcel(Bundle.java:1253)
        at android.os.Parcel.writeBundle(Parcel.java:997)
        at androidx.fragment.app.FragmentState.writeToParcel(FragmentState.java:127)
        at android.os.Parcel.writeTypedObject(Parcel.java:1634)
        at android.os.Parcel.writeTypedList(Parcel.java:1513)
        at android.os.Parcel.writeTypedList(Parcel.java:1470)
        at androidx.fragment.app.FragmentManagerState.writeToParcel(FragmentManagerState.java:58)
        at android.os.Parcel.writeParcelable(Parcel.java:1801)
        at android.os.Parcel.writeValue(Parcel.java:1707)
        at android.os.Parcel.writeArrayMapInternal(Parcel.java:928)
        at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1589)
        at android.os.Bundle.writeToParcel(Bundle.java:1253)
        at android.os.Parcel.writeBundle(Parcel.java:997)
        at android.os.Parcel.writeValue(Parcel.java:1698)
        at android.os.Parcel.writeArrayMapInternal(Parcel.java:928)
        at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1589)
        at android.os.Bundle.writeToParcel(Bundle.java:1253)
        at android.os.Parcel.writeBundle(Parcel.java:997)
        at android.os.Parcel.writeValue(Parcel.java:1698)
        at android.os.Parcel.writeArrayMapInternal(Parcel.java:928)
        at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1589)
        at android.os.Bundle.writeToParcel(Bundle.java:1253)
        at android.app.IActivityTaskManager$Stub$Proxy.activityStopped(IActivityTaskManager.java:4545)
        at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:145)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7682)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
     Caused by: java.io.NotSerializableException: com.maxkeppeler.sheets.options.OptionsSheet
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1240)
        at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1604)
        at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1565)
        at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1488)
        at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1234)
        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:354)
        at android.os.Parcel.writeSerializable(Parcel.java:1828)
        at android.os.Parcel.writeValue(Parcel.java:1780) 
        at android.os.Parcel.writeArrayMapInternal(Parcel.java:928) 
        at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1589) 
        at android.os.Bundle.writeToParcel(Bundle.java:1253) 
        at android.os.Parcel.writeBundle(Parcel.java:997) 
        at androidx.fragment.app.FragmentState.writeToParcel(FragmentState.java:127) 
        at android.os.Parcel.writeTypedObject(Parcel.java:1634) 
        at android.os.Parcel.writeTypedList(Parcel.java:1513) 
        at android.os.Parcel.writeTypedList(Parcel.java:1470) 
        at androidx.fragment.app.FragmentManagerState.writeToParcel(FragmentManagerState.java:58) 
        at android.os.Parcel.writeParcelable(Parcel.java:1801) 
        at android.os.Parcel.writeValue(Parcel.java:1707) 
        at android.os.Parcel.writeArrayMapInternal(Parcel.java:928) 
        at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1589) 
        at android.os.Bundle.writeToParcel(Bundle.java:1253) 
        at android.os.Parcel.writeBundle(Parcel.java:997) 
        at android.os.Parcel.writeValue(Parcel.java:1698) 
        at android.os.Parcel.writeArrayMapInternal(Parcel.java:928) 
        at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1589) 
        at android.os.Bundle.writeToParcel(Bundle.java:1253) 
        at android.os.Parcel.writeBundle(Parcel.java:997) 
        at android.os.Parcel.writeValue(Parcel.java:1698) 
        at android.os.Parcel.writeArrayMapInternal(Parcel.java:928) 
        at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1589) 
        at android.os.Bundle.writeToParcel(Bundle.java:1253) 
        at android.app.IActivityTaskManager$Stub$Proxy.activityStopped(IActivityTaskManager.java:4545) 
        at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:145) 
        at android.os.Handler.handleCallback(Handler.java:883) 
        at android.os.Handler.dispatchMessage(Handler.java:100) 
        at android.os.Looper.loop(Looper.java:214) 
        at android.app.ActivityThread.main(ActivityThread.java:7682) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)```
maxkeppeler commented 3 years ago

I couldn't figure out why it wants to serialize the class OptionsSheet (or another) in the first place - and for what reason only specific configurations or sheet types lead to this Exception, while other keep working. Thanks a lot for this issue. If you have any idea, just let me know. When I have some more free time, I will look into this.

bartez commented 3 years ago

I don't know how the mechanism of lambda serialization works in kotlin, but the problem is serializable listeners on saveInstanceState. Android trying to serialize MainActivity and that's reason why app crashes: obraz

obraz

After comment all lines with listeners and icons buttons - all works fine (activity is not destroyed when we go app switcher).

Of course if Activity is destroyed all listeners were gone. (you can simulate that by use split screen).

maxkeppeler commented 3 years ago

That's interesting. I assume the working examples in the sample just didn't make use of a lambda listener then.

Seems like I have to create wrapper classes that are parcalabe / serializable and pass the lambda functions before adding them to the Bundle. On the other hand I could just replace them with (functional) interfaces that are parcalabe / serializable, which would save me some additional classes.

I tend to decide for the latter. What do you think?

(Using lambda functions seemed so convenient previously...)

bartez commented 3 years ago

I tried creating classes for listeners with serializable interfaces and it doesn't work (same error). It still have reference to MainActivity which cannot be serialized. I research some articles about serialization of listeners and people re-registering listeners. I use Material Dialogs(https://github.com/afollestad/material-dialogs) library in my other project and this library doesn't restore sheets after onPause(). Maybe this constraint is the reason why.

I found this description about listener serialization:

The listener variable is a reference to an object instance reference: https://stackoverflow.com/a/36036818

I think sheets should be recreating by app not library.

maxkeppeler commented 3 years ago

That's all I could find as well. It makes sense that listeners are not serializable. However, from a convenience standpoint it certainly would be nice if a dialog or bottom-sheet could recover from a configuration change. :)

I agree with you - I will be removing the auto-restoration of the sheets.