skydoves / Balloon

:balloon: Modernized and sophisticated tooltips, fully customizable with an arrow and animations for Android.
https://skydoves.github.io/libraries/balloon/html/balloon/com.skydoves.balloon/index.html
Apache License 2.0
3.69k stars 289 forks source link

Balloon crashes on showing, BadTokenException #44

Closed dpeters-ipc closed 4 years ago

dpeters-ipc commented 4 years ago

We are using the newest Balloon version 1.1.5 in our app.

I just saw a crash in crashlytics and cannot reproduce it on my own, but it occurred 9 times until now.

Android-Versions: no specific version

Stacktrace: Fatal Exception: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running? at android.view.ViewRootImpl.setView(ViewRootImpl.java:1122) at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:450) at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:95) at android.widget.PopupWindow.invokePopup(PopupWindow.java:1621) at android.widget.PopupWindow.showAsDropDown(PopupWindow.java:1456) at android.widget.PopupWindow.showAsDropDown(PopupWindow.java:1412) at com.skydoves.balloon.Balloon$showAlignBottom$$inlined$show$2.run(Balloon.java:970) at android.os.Handler.handleCallback(Handler.java:883) at android.os.Handler.dispatchMessage(Handler.java:100) at android.os.Looper.loop(Looper.java:237) at android.app.ActivityThread.main(ActivityThread.java:8016) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1076) at com.android.internal.os.Device.fp(Device.java)

Could you at least catch this exception, so it won't crash the app? Thank you very much!

skydoves commented 4 years ago

Don't use the applicationcontext for building the balloon. Instead, use your activity's context.

dpeters-ipc commented 4 years ago

I use getContext() of Fragment

skydoves commented 4 years ago

As you can see from the exception, the balloon could not get the activity's context. It should be called in onViewCreated (after onCreateView) and it will be better to check the activity is still alive.

val activity = getActivity() ?: return
showAsDropDown..
dpeters-ipc commented 4 years ago

Hi, I tried this in a new release, but it wasn't enough. The crash still occurs. I will add !activity.isDestroyed() && !activity.isFinishing() in the next release.

But it could still happen because you call PopupWindow.showAsDropDown from a Handler. This is asynchronous and the activity-state might have changed. So some checks in the Balloon code would be an improvement for stability I think.

skydoves commented 4 years ago

@dpeters-ipc Do you use setLifecycleOwner using the activity's lifecycle as a parameter?

dpeters-ipc commented 4 years ago

I'm using setLifecycleOwner(this) where this is the Fragment instance

skydoves commented 4 years ago

@dpeters-ipc Hi, I changed the Handler using the main looper in the new version 1.1.6. If the same issue will happen and if the crashes are serious, the dismissWithDelay functionality will be removed. Thank you :)

egorikftp commented 4 years ago

@dpeters-ipc Could you please in Fragment try to use viewLifecycleOwner?

skydoves commented 4 years ago

It's included in the new version 1.1.8. Thank you for your issue :)

egorikftp commented 4 years ago

@skydoves Looks like with viewLifecycleOwner we have crash in the latest version using by balloon() delegate.

java.lang.IllegalStateException: Can't access the Fragment View's LifecycleOwner when getView() is null i.e., before onCreateView() or after onDestroyView() at androidx.fragment.app.Fragment.getViewLifecycleOwner(Fragment.java:353)

skydoves commented 4 years ago

Hi, @egorikftp. As you can see from the log, I think the balloon is used before onCreateView() or after onDestroyView(). Could you please give me more details about your use cases?

egorikftp commented 4 years ago

I call balloon after onViewCreated(), not reproduced on 1.1.7 version.

Source code: https://github.com/egorikftp/Lady-happy-Android/blob/5fbfc4c2d43b6a725a26d7c1edb290881de46da4/sources/feature/catalog/src/full/java/com/egoriku/ladyhappy/catalog/subcategory/presentation/fragment/SubCategoryFragment.kt#L37

class SubCategoryFragment : Fragment(R.layout.fragment_catalog) {

    private val binding: FragmentCatalogBinding by viewBinding()

    private val viewHolderBalloon by balloon(ViewHolderBalloonFactory::class)

    private var subcategoryController: SubCategoryController by Delegates.notNull()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        subcategoryController = SubCategoryController(
                onCatalogItemClick = {

                },
                onTrendingClick = {
                    viewHolderBalloon?.showAlignLeft(it)
                }
        )
    }
}
skydoves commented 4 years ago

@egorikftp I will check about it more and I will try to fix it in the next release. Thank you for your issue :)