Caixa de diálogo lançando "Não é possível adicionar janela - o token nulo não é para um aplicativo" com getApplication () como contexto


665

Minha atividade está tentando criar um AlertDialog que requer um contexto como parâmetro. Isso funciona como esperado se eu usar:

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

No entanto, desconfio de usar "this" como um contexto devido ao potencial de vazamento de memória quando a Activity é destruída e recriada, mesmo durante algo simples como uma rotação de tela. Em uma postagem relacionada no blog do desenvolvedor do Android :

Existem duas maneiras fáceis de evitar vazamentos de memória relacionados ao contexto. O mais óbvio é evitar escapar do contexto fora de seu próprio escopo. O exemplo acima mostrou o caso de uma referência estática, mas as classes internas e sua referência implícita à classe externa podem ser igualmente perigosas. A segunda solução é usar o contexto do aplicativo. Esse contexto permanecerá enquanto seu aplicativo estiver ativo e não depende do ciclo de vida das atividades. Se você planeja manter objetos de vida longa que precisam de um contexto, lembre-se do objeto do aplicativo. Você pode obtê-lo facilmente chamando Context.getApplicationContext () ou Activity.getApplication ().

Mas para o AlertDialog()nem getApplicationContext()ou getApplication()é aceitável como um contexto, pois lança a exceção:

"Não foi possível adicionar a janela - o token nulo não é para um aplicativo"

por referências: 1 , 2 , 3 , etc.

Então, isso realmente deve ser considerado um "bug", uma vez que somos oficialmente aconselhados a usar Activity.getApplication()e ainda assim não funciona como anunciado?

Jim


referência para o primeiro item onde R.Guy aconselha a utilização getApplication: android-developers.blogspot.com/2009/01/...
gymshoe




Respostas:


1354

Em vez de getApplicationContext(), basta usar ActivityName.this.


67
Ótimo! Apenas para comentar sobre isso ... às vezes você pode precisar armazenar "this" globalmente (por exemplo) para acessá-lo no método implementado por um ouvinte que possui seu próprio 'this'. Nesse caso, você definiria "Contexto de contexto" globalmente e, em seguida, no onCreate, definir "context = this" e depois se referir a "contexto". Espero que isso seja útil também.
Steven L

8
Na verdade, como Listeneraulas são muitas vezes anonymous-interior, eu só tendem a fazer final Context ctx = this;e eu estou longe;)
Alex

28
@StevenL Para fazer o que você está dizendo, você deve usar o ExternalClassName.this para se referir explicitamente a "this" da classe externa.
Artem Russakovskii

11
O uso de "this" não vazaria se sua caixa de diálogo fosse usada em um retorno de chamada e você deixasse a atividade antes que o retorno de chamada fosse chamado? Pelo menos é disso que o Android parece reclamar no logcat.
Artem Russakovskii

6
Eu não recomendaria a abordagem @StevenLs, pois você pode vazar facilmente a memória dessa atividade, a menos que lembre-se de limpar a referência estática no onDestroy - Artem está correto. Abordagem StevenLs é bourne de uma falta de compreensão de como Java funciona
Dori

192

Usar thisnão funcionou para mim, mas MyActivityName.thisfuncionou. Espero que isso ajude quem não conseguiu thistrabalhar.


63
É o que acontece quando você usa thisde dentro de uma classe interna. Se você deseja fazer referência à instância de uma classe externa, deve especificar isso, como faz com OuterClass.this. Apenas usar thissempre faz referência à instância da classe mais interna.
24512 kaka

60

Você pode continuar usando getApplicationContext(), mas antes de usar, adicione este sinalizador: dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)e o erro não será exibido.

Adicione a seguinte permissão ao seu manifesto:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

1
Recebo Não é possível adicionar android.view.ViewRootImpl$W@426ce670 janela - permissão negada para este tipo de janela
Ram G.

adicione permissão: <uses-permission android: name = "android.permission.SYSTEM_ALERT_WINDOW" />
codezjx

3
Parece que você não pode ativar esta permissão na API 23 em diante code.google.com/p/android-developer-preview/issues/…
roy zhang

1
Você pode usá-lo para a API 23 em diante, mas precisa solicitar ao usuário: startActivityForResult (new Intent (Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse ("pacote:" + getPackageName ())), OVERLAY_PERMISSION_REQ_CODE); No entanto, se você deve usá-lo é outra questão ...
Ben Neill

2
Isso é útil quando você está mostrando serviço de progresso dentro de diálogo
Anand Savjani

37

Você identificou corretamente o problema quando disse "... para o AlertDialog (), nem o getApplicationContext () nem o getApplication () são aceitáveis ​​como um contexto, pois gera a exceção: 'Não é possível adicionar o token de janela nulo uma aplicação'"

Para criar um Diálogo, você precisa de um Contexto de Atividade ou de Contexto de Serviço , não de Contexto de Aplicativo (getApplicationContext () e getApplication () retornam um Contexto de Aplicativo).

Veja como você obtém o Contexto de Atividade :

(1) Em uma atividade ou serviço:

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

(2) Em um fragmento: AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

O vazamento de memória não é um problema intrínseco à referência "this", que é a referência de um objeto para si mesma (ou seja, referência à memória alocada real para armazenar os dados do objeto). Isso acontece com qualquer memória alocada para a qual o Garbage Collector (GC) não consegue liberar depois que a memória alocada ultrapassa sua vida útil.

Na maioria das vezes, quando uma variável sai do escopo, a memória é recuperada pelo GC. No entanto, vazamentos de memória podem ocorrer quando a referência a um objeto mantido por uma variável, digamos "x", persiste mesmo depois que o objeto ultrapassa sua vida útil. A memória alocada será, portanto, perdida enquanto "x" mantiver uma referência a ela, porque o GC não liberará a memória enquanto essa memória ainda estiver sendo referenciada. Às vezes, vazamentos de memória não são aparentes devido a uma cadeia de referências à memória alocada. Nesse caso, o GC não liberará a memória até que todas as referências a essa memória tenham sido removidas.

Para evitar vazamentos de memória, verifique seu código quanto a erros lógicos que fazem com que a memória alocada seja referenciada indefinidamente por "this" (ou outras referências). Lembre-se de verificar também as referências em cadeia. Aqui estão algumas ferramentas que você pode usar para ajudá-lo a analisar o uso da memória e encontrar esses vazamentos de memória traquinas:


Para uma atividade que você também pode usar ActivityName.this onde ActivityName é (obviamente) o nome de sua atividade (por exemplo MainActivity)
Luis Cabrera Benito

34

Sua caixa de diálogo não deve ser um "objeto de longa duração que precise de um contexto". A documentação é confusa. Basicamente, se você fizer algo como:

static Dialog sDialog;

(observe a estática )

Então, em uma atividade em algum lugar que você fez

 sDialog = new Dialog(this);

Você provavelmente vazaria a atividade original durante uma rotação ou similar que destruiria a atividade. (A menos que você limpe o onDestroy, mas nesse caso você provavelmente não tornaria o objeto Dialog estático)

Para algumas estruturas de dados, faria sentido torná-las estáticas e baseadas no contexto do aplicativo, mas geralmente não para itens relacionados à interface do usuário, como diálogos. Então, algo como isto:

Dialog mDialog;

...

mDialog = new Dialog(this);

Está bom e não deve vazar a atividade, pois o mDialog seria liberado com a atividade, pois não é estático.


eu estou chamando-o de um AsyncTask, isso funcionou para mim, companheiro de THX
MemLeak

meu diálogo era estático, depois que eu removi a declaração estática que funcionava.
Ceddy Muhoza

25

Eu tive que enviar meu contexto através de um construtor em um adaptador personalizado exibido em um fragmento e tive esse problema com getApplicationContext (). Eu o resolvi com:

this.getActivity().getWindow().getContext()no onCreateretorno de chamada dos fragmentos .


4
Isso funcionou para mim também, eu o passei para o construtor de AsyncTask externo que estou usando (mostra um diálogo em andamento).
Rohan Kandwal 14/10

3
esta é a resposta REAL para tarefas mais complexas :) #
511

1
Eu concordo com o @teejay
Erdi İzgi

23

em Atividade, basta usar:

MyActivity.this

no fragmento:

getActivity();

Isso corrigiu para mim na minha Atividade. Obrigado
Werner

20

Em Activityno clique do botão que mostra uma caixa de diálogo

Dialog dialog = new Dialog(MyActivity.this);

Trabalhou para mim.


19

***** versão kotlin *****

Você deve passar em this@YourActivityvez de applicationContextoubaseContext


18

Pequeno hack: você pode evitar destruir a sua actividade por GC (você não deve fazê-lo, mas pode ajudar em algumas situações Não se esqueça de set. contextForDialogPara nullquando já não é necessário):

public class PostActivity extends Activity  {
    ...
    private Context contextForDialog = null;
    ...
    public void onCreate(Bundle savedInstanceState) {
        ...
        contextForDialog = this;
    }
    ...
    private void showAnimatedDialog() {
        mSpinner = new Dialog(contextForDialog);
        mSpinner.setContentView(new MySpinner(contextForDialog));
        mSpinner.show();
    }
    ...
}

@MurtuzaKabul Ele funciona porque esta PostActivity == que herda de Activity> que herda de Contexto, então quando você passar o diálogo seu contexto você está realmente passando a atividade
Elad Gelman

13

Se você estiver usando um fragmento e usando a mensagem AlertDialog / Toast, use getActivity () no parâmetro context.

como isso

ProgressDialog pdialog;
pdialog = new ProgressDialog(getActivity());
pdialog.setCancelable(true);
pdialog.setMessage("Loading ....");
pdialog.show();

12

Basta usar o seguinte:

PARA USUÁRIOS JAVA

Caso você esteja usando atividade -> AlertDialog.Builder builder = new AlertDialog.Builder(this);

OU

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

Caso você esteja usando fragmento -> AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

PARA USUÁRIOS KOTLIN

Caso você esteja usando atividade -> val builder = AlertDialog.Builder(this)

OU

val builder = AlertDialog.Builder(this@your_activity.this)

Caso você esteja usando fragmento -> val builder = AlertDialog.Builder(activity!!)


9

adicionando

dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);

e

"android.permission.SYSTEM_ALERT_WINDOW"/> em manifesto

Trabalha para mim agora. Depois de fechar e abrir o aplicativo, deu-me o erro naquele momento.


9

Eu estava usando ProgressDialogum fragmento e estava recebendo esse erro ao passar getActivity().getApplicationContext()como o parâmetro construtor. Mudá-lo para getActivity().getBaseContext()também não funcionou.

A solução que funcionou para mim foi passar getActivity(); ie

progressDialog = new ProgressDialog(getActivity());


6

Usar MyDialog md = new MyDialog(MyActivity.this.getParent());


6

Se você estiver fora da atividade, precisará usar em sua função "NameOfMyActivity.this" como atividade de atividade, exemplo:

public static void showDialog(Activity activity) {
        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        builder.setMessage("Your Message")
        .setPositiveButton("Yes", dialogClickListener)
        .setNegativeButton("No", dialogClickListener).show();
}


//Outside your Activity
showDialog(NameOfMyActivity.this);

5

Se você estiver usando um fragmento e uma AlertDialog / Toastmensagem, use getActivity()no parâmetro context

Trabalhou para mim.

Felicidades!


5

Tente usar o contexto de uma atividade que estará sob o diálogo. Mas tenha cuidado ao usar a palavra-chave "this", pois ela não funcionará sempre.

Por exemplo, se você tiver TabActivity como host com duas guias, e cada guia for outra atividade, e se tentar criar um diálogo a partir de uma das guias (atividades) e se usar "this", receberá uma exceção. O diálogo de caso deve ser conectado à atividade do host que hospeda tudo e é visível. (você pode dizer o contexto da atividade pai mais visível)

Não encontrei essas informações em nenhum documento, apenas tentando. Esta é a minha solução sem antecedentes sólidos. Se alguém com melhor conhecimento, sinta-se à vontade para comentar.


4

Para futuros leitores, isso deve ajudar:

public void show() {
    if(mContext instanceof Activity) {
        Activity activity = (Activity) mContext;
        if (!activity.isFinishing() && !activity.isDestroyed()) {
            dialog.show();
        }
    }
}


2

Ou outra possibilidade é criar o Diálogo da seguinte maneira:

final Dialog dialog = new Dialog(new ContextThemeWrapper(
            this, R.style.MyThemeDialog));

2

Acho que também pode acontecer se você estiver tentando mostrar uma caixa de diálogo a partir de um thread que não seja o principal thread da interface do usuário.

Use runOnUiThread()nesse caso.


2

Tente getParent()no argumento do contexto de contexto como novo AlertDialog.Builder(getParent());Espero que funcione, funcionou para mim.


1

Depois de dar uma olhada na API, você pode passar a caixa de diálogo sua atividade ou getActivity se estiver em um fragmento e limpá-la à força com dialog.dismiss () nos métodos de retorno para evitar vazamentos.

Embora não esteja explicitamente declarado em nenhum lugar que eu conheça, parece que você retornou a caixa de diálogo no OnClickHandlers apenas para fazer isso.


0

Se o seu Diálogo estiver criando no adaptador:

Passe a atividade ao construtor do adaptador:

adapter = new MyAdapter(getActivity(),data);

Receba no adaptador:

 public MyAdapter(Activity activity, List<Data> dataList){
       this.activity = activity;
    }

Agora você pode usar no seu Builder

            AlertDialog.Builder alert = new AlertDialog.Builder(activity);

-1

Aqui está como eu resolvi o mesmo erro para o meu aplicativo:
Adicionando a seguinte linha após criar a caixa de diálogo:

dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);  

Você não precisará adquirir um contexto. Isso é particularmente útil se você estiver exibindo outra caixa de diálogo sobre a caixa de diálogo atual atual. Ou quando não é conveniente obter um contexto.

Espero que isso possa ajudá-lo no desenvolvimento de aplicativos.

David


-1
android.support.v7.app.AlertDialog.Builder builder = new android.support.v7.app.AlertDialog.Builder(getWindow().getDecorView().getRootView().getContext());

builder.setTitle("Confirm");
builder.setMessage("Are you sure you want delete your old account?");

builder.setPositiveButton("YES", new DialogInterface.OnClickListener() {

    public void onClick(DialogInterface dialog, int which) {
        //Do nothing but close the dialog



        dialog.dismiss();

    }
});

builder.setNegativeButton("NO", new DialogInterface.OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {

        //Do nothing
        dialog.dismiss();
    }
});

android.support.v7.app.AlertDialog alert = builder.create();
alert.show();
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.