Se abre el cuadro de diálogo "No se puede agregar la ventana: el token nulo no es para una aplicación" con getApplication () como contexto


665

Mi actividad está intentando crear un AlertDialog que requiere un contexto como parámetro. Esto funciona como se esperaba si uso:

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

Sin embargo, desconfío de usar "esto" como contexto debido a la posibilidad de pérdidas de memoria cuando la actividad se destruye y se recrea incluso durante algo simple como una rotación de pantalla. De una publicación relacionada en el blog del desarrollador de Android :

Hay dos maneras fáciles de evitar pérdidas de memoria relacionadas con el contexto. La más obvia es evitar escapar del contexto fuera de su propio alcance. El ejemplo anterior mostró el caso de una referencia estática, pero las clases internas y su referencia implícita a la clase externa pueden ser igualmente peligrosas. La segunda solución es utilizar el contexto de la aplicación. Este contexto vivirá mientras su aplicación esté activa y no dependa del ciclo de vida de las actividades. Si planea mantener objetos de larga duración que necesitan un contexto, recuerde el objeto de la aplicación. Puede obtenerlo fácilmente llamando a Context.getApplicationContext () o Activity.getApplication ().

Pero para el AlertDialog()ningunogetApplicationContext() o getApplication()es aceptable como contexto, ya que arroja la excepción:

"No se puede agregar la ventana: el token nulo no es para una aplicación"

por referencias: 1 , 2 , 3 , etc.

Entonces, si esto realmente se considera un "error", ya que se nos recomienda oficialmente usar Activity.getApplication() y, sin embargo, no funciona como se anuncia?

Jim


referencia para el primer elemento donde R.Guy aconseja usar getApplication: android-developers.blogspot.com/2009/01/…
gymshoe




Respuestas:


1354

En lugar de getApplicationContext(), solo úsalo ActivityName.this.


67
¡Excelente! Solo para comentar sobre eso ... a veces es necesario almacenar "esto" globalmente (por ejemplo) para acceder a él dentro del método implementado de un oyente que tiene su propio 'esto'. En ese caso, definiría "Contexto contextual" globalmente, y luego en onCreate, establezca "context = this", y luego haga referencia a "context". Espero que eso también sea útil.
Steven L

8
En realidad, como las Listenerclases suelen ser anónimas internas, tiendo a hacerlo final Context ctx = this;y estoy fuera;)
Alex

28
@StevenL Para hacer lo que está diciendo, debe usar ExternalClassName.this para referirse explícitamente a "esto" de la clase externa.
Artem Russakovskii

11
¿El uso de "esto" no lo filtraría si su diálogo se usa en una devolución de llamada y abandona la actividad antes de que se llame la devolución de llamada? Al menos eso es de lo que Android parece quejarse en logcat.
Artem Russakovskii

66
No recomendaría el enfoque de @StevenLs, ya que puede perder fácilmente la memoria de esa actividad a menos que recuerde borrar la referencia estática en onDestroy: Artem es correcto. El enfoque de Steven nace de la falta de comprensión de cómo funciona Java
Dori

192

Usar thisno funcionó para mí, pero lo MyActivityName.thishizo. Espero que esto ayude a cualquiera que no pueda ir thisa trabajar.


63
Eso es lo que sucede cuando se usa thisdesde el interior de una clase interna. Si desea hacer referencia a la instancia de una clase externa, debe especificarlo, como lo hace con OuterClass.this. Solo usando thissiempre hace referencia a la instancia de la clase más interna.
kaka

60

Puede continuar usándolo getApplicationContext(), pero antes de usarlo, debe agregar este indicador: dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)y el error no se mostrará.

Agregue el siguiente permiso a su manifiesto:

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

1
No puedo agregar la ventana android.view.ViewRootImpl$W@426ce670 - permiso denegado para este tipo de ventana
Ram G.

agregar permiso: <usos-permiso android: name = "android.permission.SYSTEM_ALERT_WINDOW" />
codezjx

3
Parece que no puede habilitar este permiso en API 23 en adelante code.google.com/p/android-developer-preview/issues/…
roy zhang

1
Puede usarlo para la API 23 en adelante, sin embargo, debe solicitar al usuario: startActivityForResult (nueva intención (Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse ("paquete:" + getPackageName ())), OVERLAY_PERMISSION_REQ_CODE); sin embargo, si debe usarlo es otro asunto ...
Ben Neill

2
Esto es útil cuando está mostrando el diálogo de progreso dentro del servicio
Anand Savjani

37

Has identificado correctamente el problema cuando dijiste "... para AlertDialog () ni getApplicationContext () ni getApplication () son aceptables como contexto, ya que arroja la excepción: 'No se puede agregar la ventana: el token nulo no es para Una aplicación'"

Para crear un diálogo, necesita un contexto de actividad o un contexto de servicio , no un contexto de aplicación (getApplicationContext () y getApplication () devuelven un contexto de aplicación).

Así es como obtienes el contexto de actividad :

(1) En una actividad o un servicio:

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

(2) En un fragmento: AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

La pérdida de memoria no es un problema intrínseco a la referencia "este", que es la referencia de un objeto a sí misma (es decir, la referencia a la memoria asignada real para almacenar los datos del objeto). Le sucede a cualquier memoria asignada para la cual el recolector de basura (GC) no puede liberarse después de que la memoria asignada haya sobrevivido su vida útil.

La mayoría de las veces, cuando una variable queda fuera de alcance, el GC recuperará la memoria. Sin embargo, pueden producirse pérdidas de memoria cuando la referencia a un objeto sostenido por una variable, digamos "x", persiste incluso después de que el objeto haya sobrevivido su vida útil. Por lo tanto, la memoria asignada se perderá mientras "x" tenga una referencia a ella porque el GC no liberará la memoria mientras esa memoria todavía esté siendo referenciada. A veces, las pérdidas de memoria no son evidentes debido a una cadena de referencias a la memoria asignada. En tal caso, el GC no liberará la memoria hasta que se hayan eliminado todas las referencias a esa memoria.

Para evitar pérdidas de memoria, revise su código en busca de errores lógicos que causen que la memoria asignada sea referenciada indefinidamente por "esto" (u otras referencias). Recuerde verificar las referencias de la cadena también. Estas son algunas herramientas que puede usar para ayudarlo a analizar el uso de memoria y encontrar esas molestas pérdidas de memoria:


Para una Actividad, también puede usar ActivityName. Aquí donde ActivityName es (obviamente) el nombre de su actividad (por ejemplo MainActivity)
Luis Cabrera Benito

34

Su diálogo no debe ser un "objeto longevo que necesita un contexto". La documentación es confusa. Básicamente si haces algo como:

static Dialog sDialog;

(tenga en cuenta la estática )

Luego, en una actividad en algún lugar que hiciste

 sDialog = new Dialog(this);

Es probable que se filtre la actividad original durante una rotación o similar que destruiría la actividad. (A menos que limpie en onDestroy, pero en ese caso probablemente no haría que el objeto Dialog sea estático)

Para algunas estructuras de datos tendría sentido hacerlas estáticas y basadas en el contexto de la aplicación, pero generalmente no para cosas relacionadas con la interfaz de usuario, como los diálogos. Entonces algo como esto:

Dialog mDialog;

...

mDialog = new Dialog(this);

Está bien y no debería filtrar la actividad ya que mDialog se liberaría con la actividad ya que no es estática.


Lo estoy llamando desde un asinctask, esto funcionó para mí, gracias amigo
MemLeak

mi diálogo era estático, una vez que eliminé la declaración estática funcionó.
Ceddy Muhoza

25

Tuve que enviar mi contexto a través de un constructor en un adaptador personalizado que se muestra en un fragmento y tuve este problema con getApplicationContext (). Lo resolví con:

this.getActivity().getWindow().getContext()en la onCreatedevolución de llamada de los fragmentos .


44
Esto también funcionó para mí, lo pasé al constructor de AsyncTask externo que estoy usando (muestra un diálogo de progreso).
Rohan Kandwal

3
esta es la respuesta REAL para tareas más complejas :)
teejay

1
Estoy de acuerdo con @teejay
Erdi İzgi

23

en Actividad solo use:

MyActivity.this

en Fragmento:

getActivity();

Esto me lo arregló en mi Actividad. Gracias
Werner

20

Al Activityhacer clic en el botón que muestra un cuadro de diálogo

Dialog dialog = new Dialog(MyActivity.this);

Trabajó para mi.


19

***** Kotlin versión *****

Debe pasar en this@YourActivitylugar de applicationContextobaseContext


18

Pequeño truco: se puede prevenir la destrucción de su actividad por GC (que no debería hacerlo, pero puede ayudar en algunas situaciones No se olvide de conjunto. contextForDialogA nullcuando ya no es necesario):

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 Funciona porque esto == PostActivity que hereda de Activity-> que hereda de Context, así que cuando pasas el diálogo tu contexto en realidad estás pasando la actividad
Elad Gelman

13

Si está utilizando un fragmento y está utilizando el mensaje AlertDialog / Toast, use getActivity () en el parámetro de contexto.

Me gusta esto

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

12

Solo use lo siguiente:

PARA USUARIOS DE JAVA

En caso de que estés usando actividad -> AlertDialog.Builder builder = new AlertDialog.Builder(this);

O

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

En caso de que estés usando un fragmento -> AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

PARA USUARIOS DE KOTLIN

En caso de que estés usando actividad -> val builder = AlertDialog.Builder(this)

O

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

En caso de que estés usando un fragmento -> val builder = AlertDialog.Builder(activity!!)


9

agregando

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

y

"android.permission.SYSTEM_ALERT_WINDOW"/> en manifiesto

Funciona para mi ahora. Incluso después de cerrar y abrir la aplicación, me dio el error en ese momento.


9

Estaba usando ProgressDialogun fragmento y obtenía este error al pasar getActivity().getApplicationContext()como el parámetro constructor. Cambiarlo a getActivity().getBaseContext()tampoco funcionó.

La solución que funcionó para mí fue pasar getActivity(); es decir

progressDialog = new ProgressDialog(getActivity());


6

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


6

Si está fuera de la Actividad, entonces debe usar en su función "NameOfMyActivity.this" como actividad de la Actividad, por ejemplo:

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

Si está utilizando un fragmento y un AlertDialog / Toastmensaje, utilícelo getActivity()en el parámetro de contexto.

Trabajó para mi.

¡Salud!


5

Intente utilizar el contexto de una actividad que estará debajo del diálogo. Pero tenga cuidado cuando use "esta" palabra clave, porque no funcionará todo el tiempo.

Por ejemplo, si tiene TabActivity como host con dos pestañas, y cada pestaña es otra actividad, y si intenta crear un diálogo desde una de las pestañas (actividades) y si usa "this", obtendrá una excepción. el cuadro de diálogo de caso debe estar conectado a la actividad del host que aloja todo y visible. (puede decir el contexto de actividad principal más visible)

No encontré esta información en ningún documento sino al intentarlo. Esta es mi solución sin antecedentes sólidos. Si alguien con mejor conocimiento, no dude en comentar.


4

Para futuros lectores, esto debería ayudar:

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


2

O otra posibilidad es crear el diálogo de la siguiente manera:

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

2

Creo que también puede suceder si está intentando mostrar un diálogo desde un hilo que no es el hilo principal de la interfaz de usuario.

Usar runOnUiThread()en ese caso.


2

Intente getParent()en el lugar del contexto del argumento como nuevo AlertDialog.Builder(getParent());Espero que funcione, funcionó para mí.


1

Después de echar un vistazo a la API, puede pasar el diálogo a su actividad o getActivity si está en un fragmento, luego limpiarlo con fuerza con dialog.dismiss () en los métodos de retorno para evitar fugas.

Aunque no se menciona explícitamente en ningún lugar que conozca, parece que le devolvió el cuadro de diálogo en OnClickHandlers solo para hacer esto.


0

Si su diálogo se está creando en el adaptador:

Pase la actividad al constructor del adaptador:

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

Recibir en el adaptador:

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

Ahora puedes usarlo en tu Builder

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

-1

Así es como resolví el mismo error para mi aplicación:
Agregar la siguiente línea después de crear el diálogo:

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

No necesitará adquirir un contexto. Esto es particularmente útil si está apareciendo otro cuadro de diálogo sobre el cuadro de diálogo emergente actual. O cuando no es conveniente obtener un contexto.

Espero que esto pueda ayudarte con el desarrollo de tu aplicación.

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();
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.