Erreur: BinderProxy @ 45d459c0 n'est pas valide; votre activité est-elle en cours?

144

Quelle est cette erreur ... je n'ai trouvé aucune discussion sur cette erreur dans la communauté stackoverflow détaillée: -

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)
VISHAL DAGA
la source
vérifiez ce lien: Android - [Affichage des dialogues à partir des threads d'arrière-plan] ( dimitar.me/android-displaying-dialogs-from-background-threads )
Naveen
Votre lien est similaire à la réponse acceptée, mais il est beaucoup mieux expliqué. Merci pour cela
LuckyMalaka

Réponses:

344

Cela se produit probablement parce que vous essayez d'afficher une boîte de dialogue après l'exécution d'un thread d'arrière-plan, alors que l'activité est en cours de destruction.

Je voyais cette erreur signalée de temps en temps par certaines de mes applications lorsque l'activité appelant la boîte de dialogue se terminait pour une raison ou une autre lorsqu'elle tentait d'afficher une boîte de dialogue. Voici ce qui l'a résolu pour moi:

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

J'utilise cela pour contourner le problème sur les anciennes versions d'Android depuis plusieurs années maintenant, et je n'ai pas vu le crash depuis.

DiscDev
la source
1
Cela fait vraiment l'affaire! mais y a-t-il un moyen d'ouvrir la boîte de dialogue même si cela se produit? Je ne sais pas vraiment comment gérer le dialogue lorsque cela se produit. Aucune suggestion? Merci d'avance!
Carla Urrea Stabile
@CarlaStabile salut! La seule façon dont je peux penser pour afficher une boîte de dialogue lorsque cela se produit serait si vous pouvez obtenir un contexte valide pour une activité qui ne se termine pas - cela dépendra de l'endroit où vous appelez ce code et si vous avez un moyen de récupérer un contexte d'une autre activité non finale.
DiscDev
8
Mille mercis! Pour moi, le crash (avec le message d'erreur ci-dessus) se produisait lorsque j'appuyais sur le bouton Retour, avant que la boîte de dialogue ne s'affiche. Ainsi, le code continuerait et essayait de le montrer, même si j'étais dans une activité différente. Mais cela a arrêté le crash et je suis allé à la nouvelle activité avec facilité!
Azurespot
1
Il existe de nombreuses questions similaires, mais aucune d'elles n'a été suggérée. Personnellement, c'était la seule solution correcte pour mon scénario.
fillobotto
quelle est la version kotlin pour cela? isFinishing () suffira-t-il?
Alok Rajasukumaran
12

J'ai rencontré le même problème et j'ai utilisé le code proposé par DiscDev ci-dessus avec des modifications mineures comme suit:

if (!MainActivity.this.isFinishing()){
    alertDialog.show();
}
Hamza Polat
la source
J'ai changé parce que la moyenne du contexte (Activité) était MainActivity.this pour mon cas. Vous avez également raison sur le lien du profil utilisateur, mais je pensais que vous pouvez le trouver ci-dessus.
Hamza Polat du
4

si la boîte de dialogue est à l'origine de ce problème à cause du thread, vous devez l'exécuter sur le thread de l'interface utilisateur comme ça: -

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

            }
        });
Rahul Rao
la source
2

Cette erreur se produit lorsque vous affichez la boîte de dialogue pour un contexte qui n'existe plus.

Avant d'appeler, .show()vérifiez que l'activité / le contexte ne se termine pas

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

J'ai rencontré cette erreur lorsque j'avais un countDownTimerdans mon application. Il y avait une méthode appelant GameOver dans mon application comme

public void onFinish() {
     GameOver();
}

mais en fait, le jeu pourrait être terminé avant que le temps ne soit écoulé en raison d'un mauvais clic de l'utilisateur (c'était un jeu de clic). Ainsi, lorsque je regardais la boîte de dialogue Game Over après, par exemple, 20 secondes, j'ai oublié de l'annuler, countDownTimerdonc une fois le temps écoulé, la boîte de dialogue est réapparue. Ou s'est écrasé avec l'erreur ci-dessus pour une raison quelconque.

erdomester
la source
1

La solution à ce problème est assez simple. Testez simplement si l'activité passe par sa phase finale avant d'afficher la boîte de dialogue:

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

voir plus ici

Diego Venâncio
la source
1

Dans mon cas, le problème était que Contextc'était conservé comme référence faible dans la classe qui s'étend Handler. Ensuite, je passais Messenger, qui enveloppe le gestionnaire, d'un Intentà un Service. Je faisais cela à chaque fois que l'activité apparaissait à l'écran en onResume()méthode.

Donc, comme vous le comprenez, Messenger a été sérialisé avec ses champs (y compris le contexte), car c'est le seul moyen de transmettre des objets en utilisant Intent- pour les sérialiser. Au moment où Messenger a été transmis au service, l'activité elle-même n'était toujours pas prête à afficher les boîtes de dialogue car elle est dans un autre état (étant dit onResume (), c'est absolument différent du moment où l'activité est déjà à l'écran). Ainsi, lorsque Messenger a été désérialisé, le contexte était toujours à l'état de reprise, alors que l'activité était déjà à l'écran. De plus, la désérialisation alloue de la mémoire pour le nouvel objet, qui est complètement différent de celui d'origine.

La solution est simplement de se lier au service chaque fois que vous en avez besoin et de renvoyer un classeur qui a une méthode comme «setMessenger (Messenger messenger)» et de l'appeler, lorsque vous êtes lié au service.

Turkhan Badalov
la source
1

Je résous ce problème en utilisant WeakReference<Activity>comme contexte. Le crash n'est jamais réapparu. Voici un exemple de code dans Kotlin:

Classe de gestionnaire de dialogue:

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()
            }
         }

}

Et vous affichez la boîte de dialogue comme ceci:

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

Si vous voulez être super-duper protégé contre les plantages. Au lieu d' builder.create().show()utiliser:

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

Voici la safeShowméthode:

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

Il s'agit d'une méthode similaire que vous pouvez utiliser pour fermer la boîte de dialogue en toute sécurité:

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
la source
0

que diriez-vous de créer une nouvelle instance de ce dialogue que vous souhaitez appeler? En fait, je viens de rencontrer le même problème, et c'est ce que je fais. donc plutôt que:

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

que dis-tu de ça?

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

donc plutôt que de simplement vérifier s'il est sûr ou non d'afficher la boîte de dialogue, je pense que c'est beaucoup plus sûr si nous créons simplement une nouvelle instance pour afficher la boîte de dialogue.

Comme moi, dans mon cas, j'ai essayé de créer une instance (à partir d'un Fragment onCreate ) et d'appeler l'instance de ces dialogues dans un autre contenu de adapterList et cela entraînera erreur "est votre activité en cours d'exécution" . Je pensais que c'était parce que je ne créais qu'une seule instance (à partir de onCreate) et qu'elle était ensuite détruite, donc quand j'ai essayé de l'appeler à partir d'un autre adapterList, j'appelais le dialogue à partir de l'ancienne instance.

Je ne sais pas si ma solution est compatible avec la mémoire ou non, car je n'ai pas essayé de la profiler, mais cela fonctionne (enfin, c'est sûr si vous ne créez pas trop d'instances)

Dan
la source