Error: BinderProxy @ 45d459c0 no es válido; ¿se está ejecutando tu actividad?

143

¿Qué es este error? No he encontrado ninguna discusión sobre este error en la comunidad stackoverflow. Detallado: -

10-18 23:53:11.613: ERROR/AndroidRuntime(3197): Uncaught handler: thread main exiting due to uncaught exception
10-18 23:53:11.658: ERROR/AndroidRuntime(3197): android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@45d459c0 is not valid; is your activity running?
10-18 23:53:11.658: ERROR/AndroidRuntime(3197):     at android.view.ViewRoot.setView(ViewRoot.java:468)
10-18 23:53:11.658: ERROR/AndroidRuntime(3197):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:177)
10-18 23:53:11.658: ERROR/AndroidRuntime(3197):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)
10-18 23:53:11.658: ERROR/AndroidRuntime(3197):     at android.view.Window$LocalWindowManager.addView(Window.java:424)
10-18 23:53:11.658: ERROR/AndroidRuntime(3197):     at android.app.Dialog.show(Dialog.java:239)
10-18 23:53:11.658: ERROR/AndroidRuntime(3197):     at com.vishal.contacte.Locationlistener$MyLocationListener.onLocationChanged(Locationlistener.java:86)
10-18 23:53:11.658: ERROR/AndroidRuntime(3197):     at android.location.LocationManager$ListenerTransport._handleMessage(LocationManager.java:179)
10-18 23:53:11.658: ERROR/AndroidRuntime(3197):     at android.location.LocationManager$ListenerTransport.access$000(LocationManager.java:112)
10-18 23:53:11.658: ERROR/AndroidRuntime(3197):     at android.location.LocationManager$ListenerTransport$1.handleMessage(LocationManager.java:128)
10-18 23:53:11.658: ERROR/AndroidRuntime(3197):     at android.os.Handler.dispatchMessage(Handler.java:99)
10-18 23:53:11.658: ERROR/AndroidRuntime(3197):     at android.os.Looper.loop(Looper.java:123)
10-18 23:53:11.658: ERROR/AndroidRuntime(3197):     at android.app.ActivityThread.main(ActivityThread.java:4363)
10-18 23:53:11.658: ERROR/AndroidRuntime(3197):     at java.lang.reflect.Method.invokeNative(Native Method)
10-18 23:53:11.658: ERROR/AndroidRuntime(3197):     at java.lang.reflect.Method.invoke(Method.java:521)
10-18 23:53:11.658: ERROR/AndroidRuntime(3197):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:862)
10-18 23:53:11.658: ERROR/AndroidRuntime(3197):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:620)
10-18 23:53:11.658: ERROR/AndroidRuntime(3197):     at dalvik.system.NativeStart.main(Native Method)
Daga Vishal
fuente
consulte este enlace: Android - [Visualización de cuadros de diálogo de subprocesos en segundo plano] ( dimitar.me/android-displaying-dialogs-from-background-threads )
Naveen
Su enlace es similar a la respuesta aceptada, pero está mucho mejor expandido. Gracias por eso
LuckyMalaka

Respuestas:

343

Lo más probable es que esto suceda porque está intentando mostrar un cuadro de diálogo después de la ejecución de un hilo de fondo, mientras se destruye la Actividad.

Estaba viendo este error reportado de vez en cuando en algunas de mis aplicaciones cuando la actividad que llamaba al diálogo estaba terminando por alguna razón u otra cuando intentaba mostrar un diálogo. Esto es lo que me resolvió:

if(!((Activity) context).isFinishing())
{
    //show dialog
}

He estado usando esto para solucionar el problema en versiones anteriores de Android durante varios años, y no he visto el bloqueo desde entonces.

DiscDev
fuente
1
¡Esto realmente hace el truco! pero, ¿hay alguna manera de abrir el diálogo incluso si eso sucede? No estoy realmente seguro de cómo administrar el diálogo cuando esto sucede. ¿Alguna sugerencia? ¡Gracias por adelantado!
Carla Urrea Stabile
@CarlaStabile hola! La única forma en que puedo pensar en mostrar un diálogo cuando esto sucede es si puede obtener un Contexto válido para una actividad que no está terminando; dependería de dónde llame este código y si tiene una forma de recuperar un contexto de otra actividad que no termina.
DiscDev
8
¡Un millón de gracias! Para mí, el bloqueo (con el mensaje de error anterior) sucedería cuando presioné el botón Atrás, antes de que apareciera el cuadro de diálogo. Entonces el código continuaría y trataría de mostrarlo, a pesar de que estaba en una actividad diferente. ¡Pero esto detuvo el choque y fui a la nueva actividad con facilidad!
Azurespot
1
Hay muchas preguntas similares, pero en ninguna de ellas se sugirió esto. Personalmente, esta fue la única solución correcta para mi escenario.
fillobotto
¿Cuál es la versión de Kotlin para esto? ¿será suficiente IsFinishing ()?
Alok Rajasukumaran
12

Me enfrenté al mismo problema y usé el código propuesto por DiscDev arriba con pequeños cambios de la siguiente manera:

if (!MainActivity.this.isFinishing()){
    alertDialog.show();
}
Hamza Polat
fuente
cambié porque la media del contexto (Actividad) era MainActivity. Esto para mi caso. También tienes razón sobre el enlace del perfil de usuario, pero pensé que puedes encontrarlo arriba.
Hamza Polat
4

si el cuadro de diálogo está generando este problema debido al subproceso, debe ejecutar esto en el subproceso de la interfaz de usuario de esta manera:

runOnUiThread(new Runnable() {
            @Override
            public void run() {
                dialog.show();

            }
        });
Rahul Rao
fuente
2

Este error se produce cuando muestra el cuadro de diálogo para un contexto que ya no existe.

Antes de llamar .show()verifique que la actividad / contexto no esté terminando

if (!(context instanceof Activity && ((Activity) context).isFinishing())) {
    alert.show();
}
akhilesh0707
fuente
1

Encontré este error cuando tenía un countDownTimeren mi aplicación. Tenía un método que llamaba GameOver en mi aplicación como

public void onFinish() {
     GameOver();
}

pero en realidad el juego podría terminar antes de que se agotara el tiempo debido a un clic incorrecto del usuario (era un juego de clic). Entonces, cuando estaba mirando el cuadro de diálogo Game Over después de, por ejemplo, 20 segundos, olvidé cancelarlo, countDownTimerasí que una vez que se acabó el tiempo, el cuadro de diálogo apareció nuevamente. O se bloqueó con el error anterior por alguna razón.

erdomester
fuente
1

La solución a esto es bastante simple. Simplemente pruebe si la Actividad está pasando por su fase de finalización antes de mostrar el Diálogo:

  private Handler myHandler = new Handler() {
  @Override
  public void handleMessage(Message msg) {
    switch (msg.what) {
      case DISPLAY_DLG:
        if (!isFinishing()) {
        showDialog(MY_DIALOG);
        }
      break;
    }
  }
};

ver más aquí

Diego Venâncio
fuente
1

En mi caso, el problema fue que Contextse mantuvo como una referencia débil en la clase que se extiende Handler. Luego estaba pasando Messenger, que envuelve el controlador, de un Intenta a Service. Estaba haciendo esto cada vez que la actividad apareció en la pantalla en el onResume()método.

Entonces, como comprenderán, Messenger se serializó junto con sus campos (incluido el contexto), porque es la única forma de pasar objetos usando Intent: serializarlos. En ese momento cuando Messenger pasó al servicio, la actividad en sí aún no estaba lista para mostrar diálogos, ya que está en otro estado (dicho en Resume (), que es absolutamente diferente de cuando la actividad ya está en la pantalla). Entonces, cuando Messenger se deserializó, el contexto todavía estaba en el estado de reanudación, mientras que la actividad ya estaba en la pantalla. Además, la deserialización asigna memoria para un nuevo objeto, que es completamente diferente del original.

La solución es simplemente vincularse al servicio cada vez que lo necesite y devolver un archivador que tenga un método como 'setMessenger (Messenger messenger)' y llamarlo cuando esté vinculado al servicio.

Turkhan Badalov
fuente
1

Resuelvo este problema usando WeakReference<Activity>como contexto. El choque nunca apareció de nuevo. Aquí hay un código de muestra en Kotlin:

Clase de administrador de diálogo:

class DialogManager {

        fun showAlertDialog(weakActivity: WeakReference<Activity>) {
            val wActivity = weakActivity.get()
            wActivity?.let {
                val builder = AlertDialog.Builder(wActivity, R.style.MyDialogTheme)
                val inflater = wActivity.layoutInflater
                val dialogView = inflater.inflate(R.layout.alert_dialog_info, null)
                builder.setView(dialogView)

                // Set your dialog properties here. E.g. builder.setTitle("MyTitle")

                builder.create().show()
            }
         }

}

Y muestra el diálogo así:

 val dialogManager = DialogManager()
 dialogManager.showAlertDialog(WeakReference<Activity>(this@MainActivity))

Si quieres estar súper duper protegido de choques. En lugar de builder.create().show()usar:

val dialog = builder.create()
safeShow(weakActivity, dialog)

Este es el safeShowmétodo:

private fun safeShow(weakActivity: WeakReference<Activity>, dialog: AlertDialog?) {
        val wActivity = weakActivity.get()
        if (null != dialog && null != wActivity) {
            // Api >=17
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                if (!dialog.isShowing && !(wActivity).isFinishing && !wActivity.isDestroyed) {
                    try {
                        dialog.show()
                    } catch (e: Exception) {
                        //Log exception
                    }
                }
            } else {

                // Api < 17. Unfortunately cannot check for isDestroyed()
                if (!dialog.isShowing && !(wActivity).isFinishing) {
                    try {
                        dialog.show()
                    } catch (e: Exception) {
                        //Log exception
                    }
                }
            }
        }
    }

Este es un método similar que podría usar para cerrar el diálogo de forma segura:

private fun safeDismissAlertDialog(weakActivity: WeakReference<Activity>, dialog: AlertDialog?) {
        val wActivity = weakActivity.get()
        if (null != dialog && null != wActivity) {
            // Api >=17
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                if (dialog.isShowing && !wActivity.isFinishing && !wActivity.isDestroyed) {
                    try {
                        dialog.dismiss()
                    } catch (e: Exception) {
                        //Log exception
                    }
                }
            } else {

                // Api < 17. Unfortunately cannot check for isDestroyed()
                if (!dialog.isShowing && !(wActivity).isFinishing) {
                    try {
                        dialog.dismiss()
                    } catch (e: Exception) {
                        //Log exception
                    }
                }
            }
        }
    }
Ivo Stoyanov
fuente
0

¿qué tal si crea una nueva instancia de ese cuadro de diálogo al que desea llamar? De hecho, acabo de encontrar el mismo problema, y ​​eso es lo que hago. así que en lugar de:

if(!((Activity) context).isFinishing())
{
    //show dialog
}

¿Qué tal esto?

 YourDialog mDialog = new YourDialog();
 mDialog1.show(((AppCompatActivity) mContext).getSupportFragmentManager(), "OrderbookDialog");
                        }

así que en lugar de solo verificar si es seguro o no mostrar el diálogo, creo que es mucho más seguro si solo creamos una nueva instancia para mostrar el diálogo.

Al igual que yo, en mi caso intenté crear una instancia (desde un Fragment onCreate ) y llamar a la instancia de esos cuadros de diálogo en otro contenido de adapterList y esto dará como resultado "se está ejecutando su actividad" - error . Pensé que era porque simplemente creé una instancia (de onCreate) y luego se destruyó, así que cuando intenté llamarla desde otra lista de adaptadores, llamé al diálogo desde la instancia anterior.

No estoy seguro de si mi solución es amigable con la memoria o no, porque no he intentado perfilarla, pero funciona (bueno, seguramente, es segura si no crea demasiadas instancias)

Dan
fuente