Android: ProgressDialog.show () plante avec getApplicationContext

109

Je n'arrive pas à comprendre pourquoi cela se produit. Ce code:

mProgressDialog = ProgressDialog.show(this, "", getString(R.string.loading), true);

fonctionne très bien. Cependant, ce code:

mProgressDialog = ProgressDialog.show(getApplicationContext(), "", getString(R.string.loading), true);

lève l'exception suivante:

W/WindowManager(  569): Attempted to add window with non-application token WindowToken{438bee58 token=null}.  Aborting.
D/AndroidRuntime( 2049): Shutting down VM
W/dalvikvm( 2049): threadid=3: thread exiting with uncaught exception (group=0x4001aa28)
E/AndroidRuntime( 2049): Uncaught handler: thread main exiting due to uncaught exception
E/AndroidRuntime( 2049): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.tastekid.TasteKid/com.tastekid.TasteKid.YouTube}: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
E/AndroidRuntime( 2049):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2401)
E/AndroidRuntime( 2049):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2417)
E/AndroidRuntime( 2049):    at android.app.ActivityThread.access$2100(ActivityThread.java:116)
E/AndroidRuntime( 2049):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1794)
E/AndroidRuntime( 2049):    at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime( 2049):    at android.os.Looper.loop(Looper.java:123)
E/AndroidRuntime( 2049):    at android.app.ActivityThread.main(ActivityThread.java:4203)
E/AndroidRuntime( 2049):    at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime( 2049):    at java.lang.reflect.Method.invoke(Method.java:521)
E/AndroidRuntime( 2049):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
E/AndroidRuntime( 2049):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:549)
E/AndroidRuntime( 2049):    at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime( 2049): Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
E/AndroidRuntime( 2049):    at android.view.ViewRoot.setView(ViewRoot.java:460)
E/AndroidRuntime( 2049):    at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:177)
E/AndroidRuntime( 2049):    at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)
E/AndroidRuntime( 2049):    at android.app.Dialog.show(Dialog.java:238)
E/AndroidRuntime( 2049):    at android.app.ProgressDialog.show(ProgressDialog.java:107)
E/AndroidRuntime( 2049):    at android.app.ProgressDialog.show(ProgressDialog.java:90)
E/AndroidRuntime( 2049):    at com.tastekid.TasteKid.YouTube.onCreate(YouTube.java:45)
E/AndroidRuntime( 2049):    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1123)
E/AndroidRuntime( 2049):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2364)
E/AndroidRuntime( 2049):    ... 11 more

Des idées pourquoi cela se produit? J'appelle cela de la onCreateméthode.

Félix
la source
Si vous utilisez Fragment, stackoverflow.com/questions/24825114/…
Yeo

Réponses:

42

Quelle version d'API utilisez-vous? Si j'ai raison sur le problème, cela a été corrigé dans Android 1.6 (API version 4).

Cela ressemble à la référence d'objet qui getApplicationContext()retourne juste des points à null. Je pense que vous rencontrez un problème similaire à celui que j'ai eu en ce sens qu'une partie du code du onCreate()est en cours d'exécution avant que la fenêtre ne soit réellement construite. Cela va être un hack, mais essayez de lancer un nouveau Thread dans quelques centaines de millisecondes (IIRC: 300-400 semble fonctionner pour moi, mais vous devrez bricoler) qui ouvre votre ProgressDialog et démarre tout ce dont vous avez besoin ( ex. réseau IO). Quelque chose comme ça:

@Override
public void onCreate(Bundle savedInstanceState) {
    // do all your other stuff here

    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            mProgressDialog = ProgressDialog.show(
               YouTube.this.getApplicationContext(), "",
               YouTube.this.getString(R.string.loading), true);

            // start time consuming background process here
        }
    }, 1000); // starting it in 1 second
}
Jeremy Logan
la source
6
J'utilise 1.6. Je suis à peu près sûr que toutes les opérations d'interface utilisateur doivent être effectuées dans le thread de l'interface utilisateur, donc appeler ProgressDialog.show () dans un thread séparé pourrait facilement être un gros problème. Je pense toujours que c'est bizarre.
Felix
3
Dans l'exemple que je vous ai donné, n'effectuez pas d'opérations d'interface utilisateur dans un autre thread, l'autre thread rappelle simplement le thread d'interface utilisateur en lui disant d'ouvrir la boîte de dialogue.
Jeremy Logan
C'est vraiment bizarre et c'est un hack total, mais cela a fonctionné pour moi. J'ai besoin de tester le bogue que j'avais dans 1.6 pour voir si je peux ARRÊTER de l'utiliser.
Jeremy Logan
1
Ce n'est pas «bizarre» ni un «hack total», c'est une approche parfaitement légitime!
KomodoDave
1
@KomodoDave Je viens de réaliser que j'ai le même problème. Cependant, le mauvais hack est dans la minuterie. Il s'agit d'une fenêtre de synchronisation en attente d'échec intermittent dans certaines situations. La clé du succès peut être de définir un minuteur plus court, de vérifier si l'application est prête, de relancer l'action jusqu'à ce qu'elle le soit. Limitez probablement le nombre de fois à essayer également.
Jim Rush
129

J'utilise la version 2.1 d'Android avec le niveau d'API 7. J'ai rencontré ce problème (ou similaire) et résolu en utilisant ceci:

Dialog dialog = new Dialog(this);

au lieu de cela:

Dialog dialog = new Dialog(getApplicationContext());

J'espère que cela t'aides :)

Taner
la source
4
J'ai eu un problème similaire, mais j'utilisais un ActivityGroup. La seule façon dont j'ai pu résoudre cette erreur était d'utiliser getParent () à la place.
brack
20
Comment cela répond-il à sa question? Il demande POURQUOI le second ne fonctionne pas, tandis que le premier fonctionne.
Burkhard
3
-1, 'this' est identique à 'getApplicationContext ()' pour certains d'entre nous.
kellogs du
Encore une astuce utile en développement pour 2.1
Carlos P
64

Pour moi, j'ai travaillé en changeant

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

à

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

Ce qui est étrange, c'est que le premier se trouve dans le didacticiel Google et que les gens obtiennent une erreur à ce sujet.

wtk
la source
1
seule solution qui fonctionne pour moi avec une alerte dans un événement onclick
Tobias
C'est la bonne façon de procéder. N'utilisez jamais ApplicationContext à moins que cela ne soit strictement nécessaire et surtout, ne l'utilisez jamais pour afficher des éléments d'interface utilisateur. C'est à cela que servent les activités (et finalement les fragments).
Martin Marconcini
C'est la bonne. thistout seul ne fonctionnera pas si vous faites cela dans un écouteur de clic, par exemple.
Ayman Salah
23

Je ne pense pas que ce soit un problème de synchronisation autour d'un contexte d'application nul

Essayez d'étendre l'application dans votre application (ou utilisez-la simplement si vous l'avez déjà)

public class MyApp extends Application

Rendez l'instance disponible en tant que singleton privé. Ce n'est jamais nul

private static MyApp appInstance;

Créer un assistant statique dans MyApp (qui utilisera le singleton)

    public static void showProgressDialog( CharSequence title, CharSequence message )
{
    prog = ProgressDialog.show(appInstance, title, message, true); // Never Do This!
}

BOOM!!

Consultez également la réponse de l'ingénieur Android ici: WindowManager $ BadTokenException

Une des causes de cette erreur peut être d'essayer d'afficher une fenêtre / boîte de dialogue d'application via un contexte qui n'est pas une activité.

Maintenant, je suis d'accord, cela n'a pas de sens que la méthode prenne un paramètre Contexte, au lieu d'Activité.

alienjazzcat
la source
10

Après avoir lu les réponses ci-dessus, j'ai trouvé que pour ma situation, ce qui suit a résolu le problème.

Cela a jeté l'erreur

myButton.setOnClickListener(new OnClickListener(){
    public void onClick(View v) {
        MyDialogue dialog = new MyDialogue(getApplicationContext());
        dialog.show();              
    }
});

Sur la base des réponses précédentes suggérant que le contexte n'était pas le bon, j'ai modifié getApplicationContext () pour récupérer le contexte de la vue transmise aux boutons de la méthode onClick.

myButton.setOnClickListener(new OnClickListener(){
    public void onClick(View v) {
        MyDialogue dialog = new MyDialogue(v.getContext());
        dialog.show();              
    }
});

Je ne comprends pas complètement le fonctionnement de Java, donc je peux me tromper, mais je suppose que pour ma situation spécifique, la cause aurait pu être liée au fait que l'extrait ci-dessus a été défini dans une classe d'activité abstraite; hérité et utilisé par de nombreuses activités, cela a peut-être contribué au fait que getApplicationContext () ne retourne pas un contexte valide ?? (Juste une supposition).

Emile
la source
Les solutions les mieux notées fonctionnent probablement pour la plupart des cas, mais votre technique v.getContext () semble résoudre les cas les plus tenaces comme le mien. Mes connaissances empiriques sur java m'amènent à penser qu'un contexte issu d'une méthode onCreated n'est pas exactement le même qu'un contexte issu de la vue d'une méthode onClick. Votez!
Josh
Cela m'a sauvé la journée!
Alejandro Luengo
6

Je crée une vue cartographique avec des superpositions détaillées. Je créais ma superposition élémentaire comme ceci à partir de ma mapActivity:

OCItemizedOverlay currentLocationOverlay = new OCItemizedOverlay(pin,getApplicationContext);

J'ai trouvé que j'obtiendrais l'exception "android.view.WindowManager $ BadTokenException: Impossible d'ajouter la fenêtre - le jeton null n'est pas pour une application" lorsque la méthode onTap de mon itemizedoverlay a été déclenchée (lorsque l'emplacement est tapé sur la mapview).

J'ai trouvé que si je passais simplement «this» au lieu de «getApplicationContext ()» à mon constructeur, le problème disparaissait. Cela semble étayer la conclusion d'alienjazzcat. bizarre.

chiffrer
la source
1
Merci, c'est exactement ce que mon problème était.
Kon
4

Pour les activités affichées dans TabActivities, utilisez getParent ()

final AlertDialog.Builder builder = new AlertDialog.Builder(getParent());

au lieu de

final AlertDialog.Builder builder = new AlertDialog.Builder(this);
salcosand
la source
3

Pour Android 2.2,
utilisez ce code:

//activity is an instance of a class which extends android.app.Activity
Dialog dialog = new Dialog(activity);

au lieu de ce code:

// this code produces an ERROR:
//android.view.WindowManager$BadTokenException: 
//Unable to add window -- token null is not for an application
Context mContext = activity.getApplicationContext();
Dialog dialog = new Dialog(mContext);

Remarque: ma boîte de dialogue personnalisée est créée en dehors de la activity.onCreateDialog(int dialogId)méthode.

jm_java
la source
3

Essayez -

AlertDialog.Builder builder = new AlertDialog.Builder(getParent());
eyal
la source
Nécessaire lorsque l'activité en cours est à l'intérieur d'un groupe d'activités
htafoya
2

J'ai eu un problème similaire avec les fragments (de compatibilité) dans lesquels l'utilisation d'un getActivity()inside le ProgressDialog.show()bloque. Je conviens que c'est à cause du timing.

Une solution possible:

mContext = getApplicationContext();

if (mContext != null) {
    mProgressDialog = ProgressDialog.show(mContext, "", getString(R.string.loading), true);
}

à la place d'utiliser

mProgressDialog = ProgressDialog.show(getApplicationContext(), "", getString(R.string.loading), true);

Placez le mContext le plus tôt possible pour lui donner plus de temps pour saisir le contexte. Il n'y a toujours aucune garantie que cela fonctionnera, cela réduit simplement la probabilité d'un crash. Si cela ne fonctionne toujours pas, vous devrez recourir au piratage de la minuterie (ce qui peut causer d'autres problèmes de synchronisation comme fermer la boîte de dialogue plus tard).

Bien sûr, si vous pouvez utiliser thisou ActivityName.this, c'est plus stable car thispointe déjà vers quelque chose. Mais dans certains cas, comme avec certaines architectures Fragment, ce n'est pas une option.

Muz
la source
2

(Pour de futures références)

Je pense que c'est parce qu'il y a des différences dans le contexte d'application et le contexte d'activité, comme expliqué ici: http://www.doubleencore.com/2013/06/context/

Ce qui signifie que nous ne pouvons pas afficher de dialogue en utilisant le contexte d'application. C'est tout.

vtloc
la source
2

Pour utiliser les boîtes de dialogue dans les activités, procédez comme suit:

private Context mContext;
private AlertDialog.Builder mBuilder;

@Override
protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     mContext = this;

     //using mContext here refering to activity context
     mBuilder = new AlertDialog.Builder(mContext);
     //...
     //rest of the code
     //...
}

Pour utiliser des boîtes de dialogue à l'intérieur de fragments, procédez comme suit:

private Context mContext;
private AlertDialog.Builder mBuilder;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
      View mRootView = inflater.inflate(R.layout.fragment_layout, container, false);
      mContext = getActivity();

      //using mContext here refering to fragment's hosting activity context
      mBuilder = new AlertDialog.Builder(mContext);
      //...
      //rest of the code
      //...
      return mRootView;
}

C'est ça ^ _ ^

blueware
la source
1

Ce que j'ai fait pour contourner ce problème a été de créer une classe de base pour toutes mes activités où je stocke des données globales. Dans la première activité, j'ai enregistré le contexte dans une variable de ma classe de base comme ceci:

Classe de base

public static Context myucontext; 

Première activité dérivée de la classe de base

mycontext = this

Ensuite, j'utilise mycontext au lieu de getApplicationContext lors de la création de boîtes de dialogue.

AlertDialog alertDialog = new AlertDialog.Builder(mycontext).create();
rainhut
la source
Dommage que cette solution n'obtienne plus de votes positifs. De toutes les solutions possibles présentées ici, c'est la seule chose qui a fonctionné pour moi dans une AsyncTask
ckn
1

Si vous appelez ProgressDialog.show () dans un fragment, le cast du mContext en Activity a fonctionné pour moi.

     ProgressDialog pd = new ProgressDialog((Activity) mContext);
Dale Julian
la source
1

C'est un problème commun. Utilisez thisplutôt que getApplicationContext() Cela devrait résoudre votre problème

Atul Kumar
la source
0

J'ai implémenté la boîte de dialogue d'alerte pour les exceptions lancées sur la vue active actuelle.

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

Compte tenu de la même fenêtre Exception.I écrire du code pour recevoir une alerte de onCreate (). Si simple , j'utilisé context = this;après setContentView()déclaration onCreate()variable de contexte method.Taken que mondial commeContext context;

L'échantillon de code est

static Context context;

 public void onCreate(Bundle savedInstanceState)  { 
        super.onCreate(savedInstanceState); 


        setContentView(R.layout.network); 
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

        context = this;
.......

L'échantillon de méthode d'alerte est

private void alertException(String execMsg){
        Log.i(TAG,"in alertException()..."+context);
        Log.e(TAG,"Exception :"+execMsg);
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
.......

Cela fonctionne très bien pour moi.En fait, j'ai recherché cette erreur sur StackOverflow, j'ai trouvé cette requête.Après avoir lu toutes les réponses de cet article, j'ai essayé de cette façon cela fonctionne.Je pensais que c'était une solution simple pour surmonter l'exception.

Merci, Rajendar

Rajendar
la source
0

si vous rencontrez un problème sur groupActivity, ne l'utilisez pas. PARENT est un statique du Parent ActivityGroup.

final AlertDialog.Builder builder = new AlertDialog.Builder(GroupActivityParent.PARENT);

au lieu de

final AlertDialog.Builder builder = new AlertDialog.Builder(getParent());
Vilvestre
la source