skydoves/Balloon

android.view.WindowManager$BadTokenException: Unable to add window -- token android.view.ViewRootImpl$W@1a464cb is not valid; is your activity running?

faizazharr opened this issue · 7 comments

Please complete the following information:

  • Library Version [v1.6.1]
  • Affected Device(s) [All]

Describe the Bug:
all balloon feature run well in another screen except ModalBottomSheet

  • Using Jetpack compose
  • display bottomsheet
  • click button in a bottomsheet to trigger ballon
  • balloon not displayed and the app is force close

Expected Behavior:

when I use balloonWindow.showAlignTop() or balloonWindow.showAlignBottom() on compose in ModalBottomSheet the balloon can display

FATAL EXCEPTION: main Process: com.example.app, PID: 26263 android.view.WindowManager$BadTokenException: Unable to add window -- token android.view.ViewRootImpl$W@1a464cb is not valid; is your activity running? at android.view.ViewRootImpl.setView(ViewRootImpl.java:1764) at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:567) at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:148) at android.widget.PopupWindow.invokePopup(PopupWindow.java:1687) at android.widget.PopupWindow.showAtLocation(PopupWindow.java:1407) at android.widget.PopupWindow.showAtLocation(PopupWindow.java:1373) at com.skydoves.balloon.Balloon.showOverlayWindow(Balloon.kt:905) at com.skydoves.balloon.Balloon.show$lambda$30(Balloon.kt:809) at com.skydoves.balloon.Balloon.$r8$lambda$yqtXp6c5cDdGPmFqxhm9SUPDAlk(Unknown Source:0) at com.skydoves.balloon.Balloon$$ExternalSyntheticLambda5.run(Unknown Source:6) at android.os.Handler.handleCallback(Handler.java:942) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:226) at android.os.Looper.loop(Looper.java:313) at android.app.ActivityThread.main(ActivityThread.java:8762) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:604) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)

Hey @faizazharr, apologies for the delayed response. It seems that you might be attaching the Balloon to the wrong lifecycle.

There are several use cases for displaying a balloon over a modal bottom sheet. However, ensure the following key points:

  1. If the Balloon should be dismissed along with the modal bottom sheet, make sure the Balloon is dismissed correctly before the modal bottom sheet is dismissed. Since the balloon uses the window token, if the token provider (modal bottom sheet) is dismissed from memory, the children will lose access to the provider's resources. You can dismiss Balloon using DisposableEffect or something like that, it must be dismissed earlier than the modal bottom sheet.

  2. If the Balloon needs to be shown regardless of the modal bottom sheet, set a valid lifecycle for the balloon as follows:

val lifecycleOwner = LocalLifecycleOwner.current
val builder = rememberBalloonBuilder {
  ..
  setLifecycleOwner(lifecycleOwner)
}

Ensure that you obtain the lifecycleOwner from a higher scope than the modal bottom sheet's lifecycle, as it must persist longer than the modal bottom sheet.

I hope these suggestions work well for your case.

Ensure that you obtain the lifecycleOwner from a higher scope than the modal bottom sheet's lifecycle, as it must persist longer than the modal bottom sheet.

Is it applicable for regular Fragment too? use lifecycleOwner instead of viewLifecycleOwner?

It really depends on the lifecycle of your component and balloon. If the balloon must belong to the lifecycle of your particular fragment, it must use viewLifecycleOwner over lifeycycleOwner.

Okay thank you. Anyway, what context should i use if i call inside Fragment or custom view?

For the Fragment, you should use viewLifecycleOwner. In case of Activity/Fragment/View, you can just use the balloon() extension, then it will automatically find the correct lifecycle owner, so you don't need to specify it manually.

private val editBalloon by balloon<EditBalloonFactory>()

Hey guys, please feel free to reopen this issue if you still face the same problem after applying my suggestion. Thank you!

still getting the same issue after adding LocalLifecycleOwner.current in BalloonBuilder.

val lifecycleOwner = LocalLifecycleOwner.current
val builder = rememberBalloonBuilder {
setLifecycleOwner(lifecycleOwner)
setArrowSize(10)
setArrowPosition(0.5f)
setArrowPositionRules(ArrowPositionRules.ALIGN_ANCHOR)
setWidth(BalloonSizeSpec.WRAP)
setHeight(BalloonSizeSpec.WRAP)
setPadding(12)
setMarginHorizontal(12)
setCornerRadius(8f)
setBackgroundColorResource(R.color.white)
setBalloonAnimation(BalloonAnimation.ELASTIC)
setIsVisibleOverlay(true)
setOverlayColorResource(R.color.app_secondary)
}