Android: ProgressDialog.show () trava com getApplicationContext


109

Não consigo entender por que isso está acontecendo. Este código:

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

funciona muito bem. No entanto, este código:

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

lança a seguinte exceção:

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

Alguma ideia de por que isso está acontecendo? Estou chamando isso do onCreatemétodo.


Se você estiver usando Fragment, stackoverflow.com/questions/24825114/…
Yeo

Respostas:


42

Qual versão da API você está usando? Se eu estiver certo sobre qual é o problema, isso foi corrigido no Android 1.6 (API versão 4).

Parece que a referência do objeto que getApplicationContext()está retornando aponta apenas para nulo. Acho que você está tendo um problema semelhante ao que eu tive, pois parte do código do onCreate()está sendo executado antes que a janela termine de ser construída. Isso vai ser um hack, mas tente lançar um novo Thread em algumas centenas de milissegundos (IIRC: 300-400 pareceu funcionar para mim, mas você precisará mexer) que abre seu ProgressDialog e inicia tudo o que você precisava ( por exemplo, rede IO). Algo assim:

@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
Estou usando 1.6. Tenho certeza de que qualquer operação de IU deve ser feita no encadeamento de IU, portanto, chamar ProgressDialog.show () em um encadeamento separado pode facilmente ser um grande problema. Ainda acho que isso é estranho.
Felix

3
No exemplo que dei, você não está fazendo operações de IU em outro thread, o outro thread está apenas chamando o thread de IU, dizendo-lhe para abrir a caixa de diálogo.
Jeremy Logan,

É definitivamente estranho e um hack total, mas funcionou para mim. Eu preciso testar o bug que estava tendo no 1.6 para ver se consigo PARAR de usar isso.
Jeremy Logan,

1
Não é 'estranho' nem um 'hack total', é uma abordagem perfeitamente legítima!
KomodoDave

1
@KomodoDave Acabei de perceber que tenho o mesmo problema. No entanto, o hack ruim está no cronômetro. Esta é uma janela de tempo esperando para falhar intermitentemente em algumas situações. A chave para o sucesso pode ser definir um cronômetro mais curto, verificar se o aplicativo está pronto, atrasar novamente a ação até que esteja. Provavelmente limitará quantas vezes tentar também.
Jim Rush,

129

Estou usando a versão 2.1 do Android com API de nível 7. Enfrentei este problema (ou semelhante) e resolvi usando este:

Dialog dialog = new Dialog(this);

em vez disso:

Dialog dialog = new Dialog(getApplicationContext());

Espero que isto ajude :)


4
Eu tive um problema semelhante, mas estava usando um ActivityGroup. A única maneira de resolver esse erro foi usando getParent ().
parêntese de

20
Como isso responde à sua pergunta? Ele pergunta POR QUE o segundo não funciona, enquanto o primeiro funciona.
Burkhard

3
-1, 'this' é exatamente o mesmo que 'getApplicationContext ()' para alguns de nós.
kellogs

Ainda uma dica útil em desenvolvimento para 2.1
Carlos P

64

Para mim funcionou mudando

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

para

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

O estranho é que o primeiro pode ser encontrado no tutorial do google e as pessoas erram nisso ..


1
única solução funcionando para mim com um alerta dentro de um evento onclick
Tobias

Esta é a maneira correta de fazer isso. Nunca use ApplicationContext a menos que seja estritamente necessário e, acima de tudo, nunca use-o para exibir elementos da IU. É para isso que servem as atividades (e, em última análise, os fragmentos).
Martin Marconcini

É esse. thissozinho não funcionará se você estiver fazendo isso dentro de um ouvinte de clique, por exemplo.
Ayman Salah

23

Não acho que seja um problema de tempo em torno de um contexto de aplicativo nulo

Tente estender o aplicativo dentro do seu aplicativo (ou apenas use-o se você já tiver)

public class MyApp extends Application

Disponibilize a instância como um singleton privado. Isso nunca é nulo

private static MyApp appInstance;

Faça um auxiliar estático em MyApp (que usará o singleton)

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

ESTRONDO!!

Além disso, verifique a resposta do engenheiro do Android aqui: WindowManager $ BadTokenException

Uma das causas desse erro pode ser tentar exibir uma janela / caixa de diálogo do aplicativo por meio de um Contexto que não é uma Atividade.

Agora, eu concordo, não faz sentido que o método pegue um parâmetro de contexto, em vez de atividade.


10

Depois de ler as respostas acima, descobri que, para minha situação, o seguinte corrigiu o problema.

Isso gerou o erro

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

Com base nas respostas anteriores que sugeriam que o contexto era o errado, alterei getApplicationContext () para recuperar o contexto da Visualização passada para o método onClick dos botões.

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

Não entendo totalmente o funcionamento do Java, então posso estar errado, mas estou supondo que, para minha situação específica, a causa pode estar relacionada ao fato de que o trecho acima foi definido em uma classe Abstract Activity; herdado e usado por muitas Activities, talvez isso tenha contribuído para o fato de getApplicationContext () não retornar um contexto válido ?? (Apenas um palpite).


As soluções com melhor pontuação provavelmente funcionam para a maioria dos casos, mas sua técnica v.getContext () parece resolver os casos mais teimosos como o meu. Meu conhecimento empírico de java me leva a pensar que um contexto vindo de um método onCreated não é exatamente o mesmo que um contexto vindo de View de um método onClick. Vote!
Josh,

Isso salvou meu dia!
Alejandro Luengo

6

Estou criando uma visualização de mapa com sobreposições detalhadas. Eu estava criando meu itemizedoverlay como este de minha mapActivity:

OCItemizedOverlay currentLocationOverlay = new OCItemizedOverlay(pin,getApplicationContext);

Descobri que obteria a exceção "android.view.WindowManager $ BadTokenException: Não foi possível adicionar janela - o token null não é para um aplicativo" exceção quando o método onTap do meu itemizedoverlay foi acionado (quando o local é tocado no mapview).

Descobri que, se simplesmente passasse 'this' em vez de 'getApplicationContext ()' para meu construtor, o problema desapareceria. Isso parece apoiar a conclusão de alienjazzcat. esquisito.


1
Obrigado, esse é exatamente o meu problema.
Kon

4

Para atividades mostradas em TabActivities, use getParent ()

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

ao invés de

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

3

Para Android 2.2,
use este código:

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

em vez deste código:

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

Observação: Minha caixa de diálogo personalizada é criada fora do activity.onCreateDialog(int dialogId)método.


3

Experimentar -

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

Necessário quando a atividade atual está dentro de um grupo de atividades
htafoya

2

Tive um problema semelhante com fragmentos (de compatibilidade) em que usar um getActivity()dentro o ProgressDialog.show()travava. Eu concordo que é por causa do tempo.

Uma possível correção:

mContext = getApplicationContext();

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

ao invés de usar

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

Coloque o mContext o mais cedo possível para dar a ele mais tempo para capturar o contexto. Ainda não há garantia de que isso funcionará, apenas reduz a probabilidade de um travamento. Se ainda não funcionar, você terá que recorrer ao hack do cronômetro (que pode causar outros problemas de tempo, como ignorar a caixa de diálogo posteriormente).

Claro, se você pode usar thisou ActivityName.this, é mais estável porque thisjá aponta para algo. Mas em alguns casos, como em certas arquiteturas Fragment, não é uma opção.


2

(Para referências futuras)

Acho que é porque há diferenças no contexto do aplicativo e no contexto da atividade, conforme explicado aqui: http://www.doubleencore.com/2013/06/context/

O que significa que não podemos mostrar o diálogo usando o contexto do aplicativo. É isso aí.


2

Para usar diálogos dentro de atividades, faça desta forma:

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

Para usar diálogos dentro de fragmentos, faça desta forma:

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

É isso ^ _ ^


1

O que fiz para contornar isso foi criar uma classe base para todas as minhas atividades, onde armazeno dados globais. Na primeira atividade, salvei o contexto em uma variável em minha classe base assim:

Classe Base

public static Context myucontext; 

Primeira atividade derivada da classe base

mycontext = this

Então eu uso mycontext em vez de getApplicationContext ao criar diálogos.

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

É uma pena que esta solução não obtenha mais votos positivos. De todas as soluções possíveis apresentadas aqui, esta é a única coisa que funcionou para mim em uma AsyncTask
ckn

1

Se você estiver chamando ProgressDialog.show () em um fragmento, converter o mContext para Activity funcionou para mim.

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

1

Esse é um problema comum. Use em thisvez de getApplicationContext() Isso deve resolver seu problema


0

Implementei a caixa de diálogo de alerta para a exceção gerada na visualização da atividade atual.

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

Dada a mesma exceção de janela. Eu escrevo o código para alerta de onCreate (). Tão simples que usei context = this;após a setContentView()instrução no onCreate()método. Pegue a variável de contexto como global, comoContext context;

Amostra de código é

static Context context;

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


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

        context = this;
.......

A amostra do método de alerta é

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

Funciona bem para mim.Na verdade eu pesquisei esse erro no StackOverflow e encontrei esta consulta.Depois de ler todas as respostas deste post, tentei dessa forma para que funcione. Achei que esta é uma solução simples para superar a exceção.

Obrigado, rajendar


0

se você tiver um problema no groupActivity, não use isso. PARENT é uma estática do ActivityGroup Pai.

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

ao invés de

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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.