Android: ProgressDialog.show () ulega awarii z getApplicationContext

109

Nie rozumiem, dlaczego tak się dzieje. Ten kod:

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

działa dobrze. Jednak ten kod:

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

zgłasza następujący wyjątek:

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

Jakieś pomysły, dlaczego tak się dzieje? Wołam to z onCreatemetody.

Felix
źródło
Jeśli używasz Fragment, stackoverflow.com/questions/24825114/…
Yeo

Odpowiedzi:

42

Której wersji interfejsu API używasz? Jeśli mam rację co do tego, na czym polega problem, to został on naprawiony w systemie Android 1.6 (wersja API 4).

Wygląda na getApplicationContext()to, że odwołanie do obiektu, które zwraca, wskazuje tylko wartość null. Myślę, że masz problem podobny do tego, który miałem, polegający na tym, że część kodu w programie onCreate()jest uruchamiana przed ukończeniem budowy okna. To będzie hack, ale spróbuj uruchomić nowy wątek za kilkaset milisekund (IIRC: 300-400 wydawało się działać dla mnie, ale będziesz musiał majstrować), który otwiera twój ProgressDialog i uruchamia wszystko, czego potrzebujesz ( np. sieciowe IO). Coś takiego:

@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
źródło
6
Używam 1.6. Jestem prawie pewien, że wszelkie operacje UI powinny być wykonywane w wątku UI, dlatego wywołanie ProgressDialog.show () w oddzielnym wątku może łatwo być dużym problemem. Nadal uważam, że to dziwne.
Felix
3
W przykładzie, który podałem, nie wykonujesz operacji UI w innym wątku, drugi wątek po prostu woła z powrotem do wątku UI, mówiąc mu, aby otworzył okno dialogowe.
Jeremy Logan
To zdecydowanie dziwne i totalny hack, ale dla mnie zadziałało. Muszę przetestować błąd, który miałem w wersji 1.6, aby zobaczyć, czy mogę ZATRZYMAĆ używanie tego.
Jeremy Logan
1
To nie jest „dziwne” ani „totalny hack”, to całkowicie uzasadnione podejście!
KomodoDave
1
@KomodoDave Właśnie zdałem sobie sprawę, że mam ten sam problem. Jednak zły hack tkwi w zegarze. Jest to okno czasowe czekające na sporadyczne awarie w niektórych sytuacjach. Kluczem do sukcesu może być ustawienie krótszego timera, sprawdzenie, czy aplikacja jest gotowa, ponowne opóźnienie działania, aż będzie gotowe. Prawdopodobnie ogranicz również liczbę prób.
Jim Rush
129

Używam systemu Android w wersji 2.1 z interfejsem API na poziomie 7. Napotkałem ten (lub podobny) problem i rozwiązałem go za pomocą tego:

Dialog dialog = new Dialog(this);

zamiast tego:

Dialog dialog = new Dialog(getApplicationContext());

Mam nadzieję że to pomoże :)

Taner
źródło
4
Miałem podobny problem, ale korzystałem z ActivityGroup. Jedynym sposobem, w jaki mogłem rozwiązać ten błąd, było użycie metody getParent ().
nawias
20
Jak to odpowiada na jego pytanie? Pyta DLACZEGO druga nie działa, a pierwsza nie.
Burkhard
3
-1, „this” jest tym samym, co „getApplicationContext ()” dla niektórych z nas.
kellogs
Nadal przydatna wskazówka dotycząca wersji 2.1
Carlos P,
64

U mnie pracował na zmianę

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

do

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

Dziwne jest to, że pierwszy z nich można znaleźć w samouczku Google i ludzie dostają w tym błąd.

wtk
źródło
1
jedyne rozwiązanie działające dla mnie z alertem w wydarzeniu onclick
Tobias,
To jest właściwy sposób, aby to zrobić. Nigdy nie używaj ApplicationContext, chyba że jest to absolutnie konieczne, a przede wszystkim nigdy nie używaj go do wyświetlania elementów UI. Do tego służą działania (i ostatecznie fragmenty).
Martin Marconcini
To jest jeden. thissam w sobie nie zadziała, jeśli robisz to na przykład w odbiorniku kliknięć.
Ayman Salah
23

Nie sądzę, że jest to problem czasowy wokół zerowego kontekstu aplikacji

Spróbuj rozszerzyć aplikację w swojej aplikacji (lub po prostu użyj jej, jeśli już masz)

public class MyApp extends Application

Udostępnij instancję jako prywatny singleton. To nigdy nie jest nieważne

private static MyApp appInstance;

Stwórz statycznego pomocnika w MyApp (który będzie używał singletona)

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

BUM!!

Sprawdź również odpowiedź inżyniera Androida tutaj: WindowManager $ BadTokenException

Jedną z przyczyn tego błędu może być próba wyświetlenia okna / okna dialogowego aplikacji za pośrednictwem kontekstu, który nie jest działaniem.

Zgadzam się, nie ma sensu, aby metoda przyjmowała parametr Context zamiast Activity.

alienjazzcat
źródło
10

Po przeczytaniu powyższych odpowiedzi stwierdziłem, że w mojej sytuacji następujące rozwiązania rozwiązały problem.

To spowodowało błąd

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

Opierając się na poprzednich odpowiedziach, które sugerowały, że kontekst jest zły, zmieniłem metodę getApplicationContext (), aby pobrać kontekst z widoku przekazanego do metody onClick przycisków.

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

Nie do końca rozumiem działanie Javy, więc mogę się mylić, ale domyślam się, że w mojej konkretnej sytuacji przyczyna mogła być związana z faktem, że powyższy fragment został zdefiniowany w klasie Abstract Activity; dziedziczone i używane przez wiele działań, być może przyczyniło się to do tego, że metoda getApplicationContext () nie zwraca prawidłowego kontekstu? (Tylko przypuszczenie).

Emile
źródło
Lepiej ocenione rozwiązania prawdopodobnie działają w większości przypadków, ale twoja technika v.getContext () wydaje się rozwiązywać najbardziej uparte przypadki, takie jak moja. Moja empiryczna wiedza o javie prowadzi mnie do wniosku, że kontekst pochodzący z metody onCreated nie jest dokładnie tym samym, co kontekst pochodzący z widoku metody onClick. Głosować!
Josh
To uratowało mi dzień!
Alejandro Luengo
6

Tworzę widok mapy z wyszczególnionymi nakładkami. Tworzyłem taką szczegółową nakładkę z mojej mapyActivity:

OCItemizedOverlay currentLocationOverlay = new OCItemizedOverlay(pin,getApplicationContext);

Zauważyłem, że otrzymuję wyjątek „android.view.WindowManager $ BadTokenException: Unable to add window - token null is not for an application”, gdy została wyzwolona metoda onTap mojego elementu itemizedoverlay (gdy lokalizacja jest dotykana w widoku mapy).

Zauważyłem, że jeśli po prostu przekazałem mojemu konstruktorowi polecenie „this” zamiast „getApplicationContext ()”, problem zniknął. Wydaje się, że potwierdza to wniosek alienjazzcat. dziwne.

cipherz
źródło
1
Dzięki, właśnie na tym polegał mój problem.
Kon
4

Dla działań wyświetlanych w TabActivities użyj getParent ()

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

zamiast

final AlertDialog.Builder builder = new AlertDialog.Builder(this);
salcosand
źródło
3

Dla Androida 2.2
Użyj tego kodu:

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

zamiast tego kodu:

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

Uwaga: Moje niestandardowe okno dialogowe jest tworzone poza activity.onCreateDialog(int dialogId)metodą.

jm_java
źródło
3

Próbować -

AlertDialog.Builder builder = new AlertDialog.Builder(getParent());
oko
źródło
Potrzebne, gdy bieżąca aktywność jest w grupie zajęć
htafoya
2

Miałem podobny problem z (kompatybilnością) fragmentami, w których użycie getActivity()wewnątrz ProgressDialog.show()powoduje awarię. Zgadzam się, że to z powodu wyczucia czasu.

Możliwa poprawka:

mContext = getApplicationContext();

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

zamiast używać

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

Umieść mContext tak wcześnie, jak to możliwe, aby dać mu więcej czasu na pobranie kontekstu. Nadal nie ma gwarancji, że to zadziała, po prostu zmniejsza prawdopodobieństwo awarii. Jeśli nadal nie działa, musisz skorzystać z hackowania timera (co może powodować inne problemy z synchronizacją, takie jak późniejsze zamknięcie okna dialogowego).

Oczywiście, jeśli możesz użyć thislub ActivityName.this, jest bardziej stabilny, ponieważ thisjuż wskazuje na coś. Ale w niektórych przypadkach, na przykład w przypadku niektórych architektur Fragment, nie jest to możliwe.

Muz
źródło
2

(Do przyszłych odniesień)

Myślę, że dzieje się tak, ponieważ istnieją różnice w kontekście aplikacji i kontekście działania, jak wyjaśniono tutaj: http://www.doubleencore.com/2013/06/context/

Co oznacza, że ​​nie możemy wyświetlić okna dialogowego przy użyciu kontekstu aplikacji. Otóż ​​to.

vtloc
źródło
2

Aby używać okien dialogowych w działaniach, zrób to w ten sposób:

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

Aby używać okien dialogowych wewnątrz fragmentów, zrób to w ten sposób:

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

To wszystko ^ _ ^

blueware
źródło
1

Aby obejść ten problem, utworzyłem klasę bazową dla wszystkich moich działań, w których przechowuję dane globalne. W pierwszym ćwiczeniu zapisałem kontekst w zmiennej w mojej klasie bazowej w następujący sposób:

Klasa podstawowa

public static Context myucontext; 

Pierwsza aktywność pochodząca z klasy bazowej

mycontext = this

Następnie podczas tworzenia okien dialogowych używam mycontext zamiast getApplicationContext.

AlertDialog alertDialog = new AlertDialog.Builder(mycontext).create();
Rainhut
źródło
Szkoda, że ​​to rozwiązanie nie ma już głosów pozytywnych. Ze wszystkich przedstawionych tutaj możliwych rozwiązań jest to jedyna rzecz, która zadziałała dla mnie w AsyncTask
ckn
1

Jeśli wywołujesz ProgressDialog.show () we fragmencie, rzutowanie mContext na Activity zadziałało.

     ProgressDialog pd = new ProgressDialog((Activity) mContext);
Dale Julian
źródło
1

To częsty problem. Użyj thiszamiast getApplicationContext() To powinno rozwiązać twój problem

Atul Kumar
źródło
0

Zaimplementowałem Alert Dialog dla wyjątków rzucających się w bieżącym widoku aktywności, kiedykolwiek tak dawałem

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

Biorąc pod uwagę ten sam wyjątek dla okna. Piszę kod dla alertów z onCreate (). Tak prosty, że użyłem context = this;po setContentView()instrukcji w onCreate()metodzie.Context context;

Przykładowy kod to

static Context context;

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


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

        context = this;
.......

Przykładem metody alertu jest

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

To działa dobrze dla mnie, Właściwie szukałem tego błędu na StackOverflow, znalazłem to zapytanie.Po przeczytaniu wszystkich odpowiedzi tego postu, próbowałem w ten sposób, więc to działa.

Dzięki, Rajendar

Rajendar
źródło
0

jeśli masz problem z groupActivity, nie używaj tego. PARENT jest statyczną z Parent ActivityGroup.

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

zamiast

final AlertDialog.Builder builder = new AlertDialog.Builder(getParent());
vilvestre
źródło