0

I have added following code to display a Popover on my spinner view. The code uses custom PopOver which extends PopupWindow :

    @Override
    public void onStart() {
        super.onStart();

            new Handler().postDelayed(new Runnable() {
                public void run() {
                    if (mActivity == null || !isAdded() || mActivity.isFinishing() || mSpinner == null) {
                        return;
                    }

                    displayPopOver();
                }
            }, 500L); // have seen BadTokenException if we try to show the popup too early
     }


   private void displayPopOver() {
    //PopOver is a custom class that extends PopupWindow
    PopOver p = PopOver.createVerticalPopOver(mActivity, PopOver.Type.TIP);
    p.show(mSpinner);
   }

I cannot reproduce the crash, but through crash reports I see the application crashes multiple times. What am I doing wrong? Can calling this code in onResume() instead of onStart() help?

android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running? 1 at android.view.ViewRootImpl.setView(ViewRootImpl.java:532) 2 at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:259) 3 at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69) 4 at android.widget.PopupWindow.invokePopup(PopupWindow.java:1019) 5 at android.widget.PopupWindow.showAtLocation(PopupWindow.java:850) 6 at android.widget.PopupWindow.showAtLocation(PopupWindow.java:814) 7 at com.PopOver.show(PopOver.java:339) 8 at com.MainFragment.displayPopOver(MainFragment.java:515) 9 at com.MainFragment.access$1300(MainFragment.java:73) 10 at com.MainFragment$5.run(MainFragment.java:497) 11 at android.os.Handler.handleCallback(Handler.java:733) 12 at android.os.Handler.dispatchMessage(Handler.java:95) 13 at android.os.Looper.loop(Looper.java:136) 14 at android.app.ActivityThread.main(ActivityThread.java:5001) 15 at java.lang.reflect.Method.invokeNative(Native Method) 16 at java.lang.reflect.Method.invoke(Method.java:515) 17 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785) 18 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601) 19 at dalvik.system.NativeStart.main(Native Method)

Priya B
  • 96
  • 10
  • Means popup is over Spinner or Activity? – ρяσѕρєя K Nov 16 '15 at 20:24
  • The popup is over spinner – Priya B Nov 16 '15 at 20:25
  • where u are creating spinner – ρяσѕρєя K Nov 16 '15 at 20:32
  • In onCreateView() but I have checked if its null before showing the PopOver – Priya B Nov 16 '15 at 20:33
  • Which API version using for project? – ρяσѕρєя K Nov 16 '15 at 20:54
  • I think your check of `isFinishing` is not exactly what you need. Actually you need to not show ht epopup in every case your activity is not on the screen. Try reproducng the exception by opening the activity and immediately pressing hte home button. Please write back if this causes hte error, I also have in mind a suggestion that should resolve your problem. – Boris Strandjev Nov 18 '15 at 07:14
  • @BorisStrandjev tried reproducing it, but I am not able to, I tried various other ways, increasing delay time, and keeping the app in background but it never crashes. – Priya B Nov 18 '15 at 17:29
  • What if you rotate the device immediately when you open the Activity – Boris Strandjev Nov 18 '15 at 18:48
  • Well it causes a different exception android.view.WindowLeaked, which means it has not been dismissed properly but this is a different issue according to me. – Priya B Nov 18 '15 at 19:02
  • One solution I thought of is using mSpinner.post method and remove the post delay. @BorisStrandjev – Priya B Nov 18 '15 at 19:05
  • mind you telling what are you trying to achieve at the end, so that i can try to suggest entirely alternative approach (if i can think of any). Regretfully, I am out of ideas how to reproduce the problem you get reported. – Boris Strandjev Nov 18 '15 at 20:35
  • @BorisStrandjev Can you please tell me the solution which you said you have in mind? – Priya B Nov 18 '15 at 21:30
  • Sorry, I had a solution in mind that would have solved the problem if it resided with inimising the application. As you say this does not reproduce your problem, it will be no good. That is why i asked you to explain in a bit further detail the functionality you want to implement and I will try to propose alternative solution... – Boris Strandjev Nov 18 '15 at 22:04
  • I want to show a popup on my spinner as a hint. – Priya B Nov 18 '15 at 22:08

2 Answers2

4

The exception being thrown indicates that you are showing the custom PopupWindow too soon.

Although the 500 ms delay may alleviate the problem on some devices, it isn't the solution. You will note that if you bump the delay to say 1000 ms, the problem will most probably disappear for good. This again, is not the solution.

For guarantee, adopt the following method:

Let the activity tell you when its ready to show your PopupWindow. In your case, this could be set up as soon as you can find the Spinner:

mSpinner = (Spinner) findViewById(R.id.spinner); 

Next, post a Runnable to it:

mSpinner.post(new Runnable() {
    public void run() {
        displayPopOver();
    }
});

No delay needed here.

Vikram
  • 51,313
  • 11
  • 93
  • 122
  • I already put this change, but instead of setting it in activity I have put it in onResume and check if mSpinner is not null before calling mSpinner.post – Priya B Nov 18 '15 at 22:53
0

Firstly:

mActivity

Ensure you are not holding on to activity reference. There are easier ways i.e. getActivity() of fragment. Which will automatically return null if your fragment is detached. isAdded() automatically returns false when fragment is in detached state. So, stop holding extra Activity references, or fragment state variables, they are not automatically updated.

and Then:

When you schedule a runnable that is supposed to do some thing with UI, then keep in mind that android Activity, Fragment or View, along with its UI can simply go away any time. So, remember to cancel that runnable from handler when UI goes away. That is, onStop() of activity and on onDestroyView() of fragments and onDetachedFromWindow() of custom views.

Also:

a better check would be:

if (getActivity() != null && isAdded() && !getActivity().isFinishing() && mSpinner != null) {
  // do stuff
}
S.D.
  • 29,290
  • 3
  • 79
  • 130