Android: ProgressDialog.show () si arresta in modo anomalo con getApplicationContext


109

Non riesco a capire perché sta succedendo. Questo codice:

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

funziona bene. Tuttavia, questo codice:

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

genera la seguente eccezione:

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

Qualche idea sul perché sta accadendo? Lo chiamo dal onCreatemetodo.


Se stai utilizzando Fragment, stackoverflow.com/questions/24825114/…
Yeo

Risposte:


42

Quale versione dell'API stai utilizzando? Se ho ragione su quale sia il problema, questo è stato risolto in Android 1.6 (versione API 4).

Sembra che il riferimento all'oggetto che getApplicationContext()sta restituendo punti a null. Penso che tu abbia un problema simile a quello che ho avuto in quanto parte del codice in onCreate()viene eseguito prima che la finestra venga effettivamente completata. Questo sarà un trucco, ma prova ad avviare un nuovo thread in poche centinaia di millisecondi (IIRC: 300-400 sembrava funzionare per me, ma dovrai armeggiare) che apre il tuo ProgressDialog e avvia qualsiasi altra cosa di cui hai bisogno ( es. rete IO). Qualcosa come questo:

@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
}

6
Sto usando 1.6. Sono abbastanza sicuro che qualsiasi operazione dell'interfaccia utente dovrebbe essere eseguita nel thread dell'interfaccia utente, quindi chiamare ProgressDialog.show () in un thread separato potrebbe facilmente essere un grosso problema. Penso ancora che sia strano.
Felix

3
Nell'esempio che ho fornito non stai eseguendo operazioni dell'interfaccia utente in un altro thread, l'altro thread sta semplicemente richiamando il thread dell'interfaccia utente dicendogli di aprire la finestra di dialogo.
Jeremy Logan

È decisamente strano ed è un trucco totale, ma ha funzionato per me. Ho bisogno di testare il bug che avevo nella 1.6 per vedere se posso SMETTERE di usarlo.
Jeremy Logan

1
Non è "strano" né è un "hack totale", è un approccio perfettamente legittimo!
KomodoDave

1
@KomodoDave Mi sono appena reso conto di avere lo stesso problema. Tuttavia, il cattivo trucco è nel timer. Questa è una finestra temporale in attesa di fallire in modo intermittente in alcune situazioni. La chiave del successo potrebbe essere l'impostazione di un timer più breve, verificare se l'applicazione è pronta, ritentare l'azione finché non lo è. Probabilmente limita anche quante volte provare.
Jim Rush

129

Sto usando Android versione 2.1 con API di livello 7. Ho affrontato questo (o simile) problema e l'ho risolto usando questo:

Dialog dialog = new Dialog(this);

Invece di questo:

Dialog dialog = new Dialog(getApplicationContext());

Spero che questo ti aiuti :)


4
Ho avuto un problema simile, ma stavo usando un ActivityGroup. L'unico modo in cui sono stato in grado di risolvere questo errore è stato utilizzare getParent () invece.
brack

20
Come risponde questo alla sua domanda? Chiede PERCHÉ il secondo non funziona, mentre il primo sì.
Burkhard

3
-1, per alcuni di noi "this" equivale a "getApplicationContext ()".
kellogs

Ancora un suggerimento utile in via di sviluppo per 2.1
Carlos P

64

Per me ha funzionato cambiando

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

per

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

La cosa strana è che il primo può essere trovato nel tutorial di Google e le persone ottengono un errore su questo ..


1
unica soluzione che funziona per me con un avviso all'interno di un evento onclick
Tobias

Questo è il modo corretto per farlo. Non utilizzare mai ApplicationContext se non strettamente necessario e, soprattutto, non utilizzarlo mai per visualizzare elementi dell'interfaccia utente. Ecco a cosa servono le attività (e in definitiva i frammenti).
Martin Marconcini

Questo è quello. thistutto da solo non funzionerà se lo fai all'interno di un, ad esempio, click listener.
Ayman Salah

23

Non credo che questo sia un problema di tempistica intorno a un contesto di applicazione nullo

Prova ad estendere l'Applicazione all'interno della tua app (o usala semplicemente se l'hai già)

public class MyApp extends Application

Rendi l'istanza disponibile come singleton privato. Questo non è mai nullo

private static MyApp appInstance;

Crea un helper statico in MyApp (che utilizzerà il singleton)

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

BOOM!!

Inoltre, controlla la risposta dell'ingegnere Android qui: WindowManager $ BadTokenException

Una causa di questo errore potrebbe essere il tentativo di visualizzare una finestra / finestra di dialogo dell'applicazione tramite un contesto che non è un'attività.

Ora, sono d'accordo, non ha senso che il metodo prenda un parametro Context, invece di Activity ..


10

Dopo aver letto le risposte di cui sopra, ho scoperto che per la mia situazione quanto segue ha risolto il problema.

Questo ha generato l'errore

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

Sulla base delle risposte precedenti che suggerivano che il contesto fosse quello sbagliato, ho cambiato getApplicationContext () per recuperare il contesto dal metodo View passato ai pulsanti onClick.

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

Non comprendo appieno il funzionamento di Java quindi potrei sbagliarmi, ma immagino che per la mia situazione specifica la causa potrebbe essere stata correlata al fatto che lo snippet di cui sopra è stato definito in una classe Abstract Activity; ereditato e utilizzato da molte attività, forse questo ha contribuito al fatto che getApplicationContext () non restituisce un contesto valido ?? (Solo una supposizione).


Le soluzioni con punteggio migliore probabilmente funzionano per la maggior parte dei casi, ma la tua tecnica v.getContext () sembra risolvere i casi più ostinati come il mio. La mia conoscenza empirica di java mi porta a pensare che un contesto proveniente da un metodo onCreated non sia esattamente lo stesso di un contesto proveniente dalla visualizzazione di un metodo onClick. Vota!
Josh

Questo mi ha salvato la giornata!
Alejandro Luengo

6

Sto creando una visualizzazione mappa con sovrapposizioni dettagliate. Stavo creando la mia sovrapposizione di elementi in questo modo dalla mia mappa Attività:

OCItemizedOverlay currentLocationOverlay = new OCItemizedOverlay(pin,getApplicationContext);

Ho scoperto che avrei ottenuto l'eccezione "android.view.WindowManager $ BadTokenException: impossibile aggiungere la finestra - il token null non è per un'applicazione" quando è stato attivato il metodo onTap del mio itemizedoverlay (quando la posizione è toccata sulla mapview).

Ho scoperto che se passavo semplicemente "this" invece di "getApplicationContext ()" al mio costruttore, il problema scompariva. Questo sembra supportare la conclusione di alienjazzcat. strano.


1
Grazie, questo è esattamente quello che era il mio problema.
Kon

4

Per le attività mostrate in TabActivities usa getParent ()

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

invece di

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

3

Per Android 2.2
Usa questo codice:

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

invece di questo codice:

// 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);

Nota: la mia finestra di dialogo personalizzata viene creata fuori dal activity.onCreateDialog(int dialogId)metodo.


3

Provare -

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

Necessario quando l'attività corrente è all'interno di un gruppo di attività
htafoya

2

Ha avuto un problema simile con i frammenti (compatibilità) in cui l'utilizzo di un getActivity()all'interno lo ProgressDialog.show()blocca. Sono d'accordo che è a causa del tempismo.

Una possibile soluzione:

mContext = getApplicationContext();

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

invece di usare

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

Posiziona mContext il prima possibile per dargli più tempo per afferrare il contesto. Non c'è ancora alcuna garanzia che funzioni, riduce solo la probabilità di un incidente. Se ancora non funziona, dovresti ricorrere al timer hack (che può causare altri problemi di temporizzazione come chiudere la finestra di dialogo in un secondo momento).

Certo, se puoi usare thiso ActivityName.this, è più stabile perché thisindica già qualcosa. Ma in alcuni casi, come con alcune architetture Fragment, non è un'opzione.


2

(Per riferimenti futuri)

Penso che sia perché ci sono differenze nel contesto dell'applicazione e nel contesto dell'attività, come spiegato qui: http://www.doubleencore.com/2013/06/context/

Ciò significa che non possiamo mostrare la finestra di dialogo utilizzando il contesto dell'applicazione. Questo è tutto.


2

Per utilizzare le finestre di dialogo all'interno delle attività, fallo in questo modo:

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
     //...
}

Per utilizzare le finestre di dialogo all'interno di frammenti, fallo in questo modo:

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;
}

Questo è tutto ^ _ ^


1

Quello che ho fatto per aggirare questo problema è stato creare una classe base per tutte le mie attività in cui memorizzo i dati globali. Nella prima attività, ho salvato il contesto in una variabile nella mia classe base in questo modo:

Classe di base

public static Context myucontext; 

Prima attività derivata dalla classe base

mycontext = this

Quindi utilizzo mycontext invece di getApplicationContext durante la creazione di finestre di dialogo.

AlertDialog alertDialog = new AlertDialog.Builder(mycontext).create();

Peccato che questa soluzione non ottenga più voti positivi. Di tutte le possibili soluzioni presentate qui, questa è l'unica cosa che ha funzionato per me in un AsyncTask
ckn

1

Se stai chiamando ProgressDialog.show () in un frammento, il cast di mContext su Activity ha funzionato per me.

     ProgressDialog pd = new ProgressDialog((Activity) mContext);

1

Questo è un problema comune. Usa thisinvece di getApplicationContext() Quello dovrebbe risolvere il tuo problema


0

Ho implementato la finestra di dialogo degli avvisi per il lancio di eccezioni nella visualizzazione delle attività correnti, ogni volta che ho dato in questo modo

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

Data la stessa eccezione della finestra, scrivo codice per alert da onCreate (). Così semplice che ho usato l' istruzione context = this;after setContentView()nel onCreate()metodo. Preso variabile di contesto come globale comeContext context;

Il codice di esempio è

static Context context;

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


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

        context = this;
.......

Il campione del metodo di avviso è

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

Funziona bene per me. In realtà ho cercato questo errore su StackOverflow ho trovato questa query. Dopo aver letto tutte le risposte di questo post, ho provato in questo modo quindi funziona. Ho pensato che questa fosse una soluzione semplice per superare l'eccezione.

Grazie, Rajendar


0

se hai un problema su groupActivity non usarlo. PARENT è una statica dal Parent ActivityGroup.

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

invece di

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.