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

Gymshoe
fuente

Respuestas:

1354

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

Steven L
fuente
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.

TrueCoke
fuente
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" />
codezjx
fuente
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:

UNO
fuente
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.

Kevin TeslaCoil
fuente
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 .

Grux
fuente
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();
Mahmoud Ayman
fuente
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.

P_Pran
fuente
19

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

Debe pasar en this@YourActivitylugar de applicationContextobaseContext

MilaDroid
fuente
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();
    }
    ...
}
Mikalai Daronin
fuente
@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();
muaaz
fuente
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!!)

Muhammad Faizan
fuente
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.

AlphaStack
fuente
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());

TM
fuente
6

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

MSA
fuente
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);
oabareload
fuente
5

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

Trabajó para mi.

¡Salud!

curlyreggie
fuente
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.

Engin OZTURK
fuente
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();
        }
    }
}
Denshov
fuente
2

En mi caso trabajo:

this.getContext();
el caminante
fuente
2

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

final Dialog dialog = new Dialog(new ContextThemeWrapper(
            this, R.style.MyThemeDialog));
Martin Koubek
fuente
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.

Erwan
fuente
2

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

Priyank Joshi
fuente
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.

G_V
fuente
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);
josedlujan
fuente
-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

us_david
fuente
-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();
BumpBitcoin
fuente