36

In my ongoing learning process (dialog boxes this time), I discovered that this works:

  AlertDialog.Builder builder = new AlertDialog.Builder(this);

While the following doesn't work (fails at runtime with WindowManager$BadTokenException):

  AlertDialog.Builder builder = new AlertDialog.Builder(this.getApplicationContext());

I don't understand why, because the constructor for AlertDialog.Builder is defined to accept Context as a parameter, not Activity:

public AlertDialog.Builder (Context context)

Constructor using a context for this builder and the AlertDialog it creates.

What am I missing?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
an00b
  • 11,338
  • 13
  • 64
  • 101
  • 2
    It's also true for other dialogs. Good question, +1 – bigstones Mar 25 '11 at 19:10
  • @bigstones I discovered another thread dealing with a similar issue but no explanation: http://stackoverflow.com/questions/3968170/android-prompt-users-input-using-a-dialog – an00b Mar 25 '11 at 19:40
  • My guess is that the Builder doesn't just ask for an Activity because it would prevent future API's from having other kinds of contexts that can display a dialog. – bigstones Mar 25 '11 at 20:21
  • @bigstones right idea, except its not just future, its now. AlertDialog.Builder can be used by ListActivity, Service, ... and any inheriting from Context exactly because its argument is for an abstract Context. – DJC Mar 29 '11 at 00:32
  • @DJC actually you can't display dialogs directly from classes inheriting from `Context` other than `Activity`. http://www.mail-archive.com/android-developers@googlegroups.com/msg49667.html – bigstones Mar 29 '11 at 12:23
  • @bigstones ahh I realize I've only done it with Activity and its subclasses, and it doesn't make sense to use it from a Service does it. In any case, I made mine work by passing the activity into shared classes as an arg. – DJC Mar 29 '11 at 20:32
  • 2
    Oddly, Google provides an example that indicates you can use getApplicationContext(). At the very bottom of this page (http://developer.android.com/guide/topics/ui/dialogs.html) they provide an AlertDialog example that includes this: Context mContext = getApplicationContext(); ... builder = new AlertDialog.Builder(mContext); Technically, it DOES allow me to do this this but my app blows up when I then try to show the dialog. Hmmm... – Robert Nekic Apr 29 '11 at 20:43

2 Answers2

27

An Activity inherits a Context. AlertDialog.Builder specifies a Context argument because it can then be used by ANY class that is a subclass of Context, including an Activity, ListActivity, Service, ... (There is a common coding idiom behind this - you can learn more about it by reading Item I8 (on Interfaces and Abstract classes) in Joshua Bloch's fantastic Effective Java).

getApplicationContext() returns the context for your application, which is mostly the same as your activities context - and the "mostly" is what is throwing you off. The details are unclear but this is a widely encountered issue, and the typical answer is to use the context that will be writing the alert to the screen. Note that that is not the one returned by getApplicationContext().

Now if you're like me, you may say "but I am working in a class that does not inherit from Activity - which is why I want to use getApplicationContext() for this in the first place - duh!" I'm actually don't speak as rudely as that ;p .. the point was I've been here too. I fixed it like so: 1) ask yourself "do I have my UI AlertDialog code in a non-activity class because I want to share it across activities .. or even across ListActivities, Services, ...?". If not, hmmm... do you really have AlertDialog UI calls in code that you can't guarantee will have access to the UI (and thus context)? If so, reconsider your design.

Presuming you do want to share this class across Activities, ... the answer becomes clear. You want your class to be usable by a variety of callers, each probably with its own context: so the caller must pass its context into your class as an argument:

myClass(Context theContext, ...) { ... }

Each activity, service, etc. then makes calls like so:

myClass(this, ...);

Look familiar?

Do be careful! that if you are sharing code, you must consider the possibility of different calls coming into your shared code in parallel, with all the many ramifications. Thats beyond our scope here...

Have fun :)

DJC
  • 3,243
  • 3
  • 27
  • 31
  • Thanks for your answer. Unfortunately, I still don't understand why AlertDialog.Builder(Context context) only accepts Activity as a parameter. Are you saying the this's context is not the same as this.getApplicationContext()? Please explain. – an00b Mar 31 '11 at 17:04
  • yes, it seems that the Activity context is a superset of the Application context - the Activity specializes the Application context by adding such things as those used by AlertDialog. FYI this is my understanding based upon reading of others findings on this same issue. The thing I am most confident of is the fis: passing in an Activity to my code worked. – DJC Mar 31 '11 at 21:05
  • 13
    The "clear" answer is still problematic, because two Activities may not necessarily only want to share the same "class", they may want to share the same "instance" of that class. If that's the case, then you can't let both Activities create the shared class with their own context. This is really a horrible design choice by Android. Why on earth would you ever want to create AlertDialogs, and not have them popup above all Activities? iOS lets you throw UIAlertViews from anywhere. Android should, too. – Nate Aug 25 '11 at 07:30
17

AlertDialog is a subclass of Dialog, which has an associated Window, which has an associated LayoutParams. One of those parameters is the window's type. The default type is TYPE_APPLICATION_ATTACHED_DIALOG, which requires a parent window.

The WindowManager associated with an Activity is configured to use the Activity's window as the parent window. The WindowManager associated with an Application has no associated parent window.

Bottom line: To successfully display a dialog, you have to either change the default window type to a type that does not require a parent window, or you must use a context that has an associated parent window.

j__m
  • 9,392
  • 1
  • 32
  • 56