Błąd: BinderProxy @ 45d459c0 jest nieprawidłowy; czy Twoja aktywność biegnie?

143

Co to za błąd ... Nie znalazłem żadnej dyskusji na temat tego błędu w społeczności stackoverflow Szczegółowe: -

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
źródło
sprawdź ten link: Android - [Wyświetlanie okien dialogowych z wątków w tle] ( dimitar.me/android-displaying-dialogs-from-background-threads )
Naveen
Twój link jest podobny do zaakceptowanej odpowiedzi, ale jest znacznie lepiej widoczny. Dzięki za to
LuckyMalaka

Odpowiedzi:

343

Dzieje się tak najprawdopodobniej, ponieważ próbujesz wyświetlić okno dialogowe po wykonaniu wątku w tle, gdy działanie jest niszczone.

Widziałem ten błąd zgłaszany od czasu do czasu z niektórych moich aplikacji, gdy działanie wywołujące okno dialogowe kończyło się z jakiegoś powodu, gdy próbowało wyświetlić okno dialogowe. Oto, co mi to rozwiązało:

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

Używam tego do obejścia problemu w starszych wersjach Androida od kilku lat i od tego czasu nie widziałem awarii.

DiscDev
źródło
1
To naprawdę działa! ale czy istnieje sposób na otwarcie okna dialogowego, nawet jeśli tak się stanie? Nie jestem pewien, jak zarządzać oknem dialogowym, gdy tak się dzieje. Jakieś sugestie? Z góry dziękuję!
Carla Urrea Stabile
@CarlaStabile hi! Jedynym sposobem, w jaki mogę pomyśleć o wyświetleniu okna dialogowego, gdy tak się stanie, byłoby uzyskanie prawidłowego kontekstu dla działania, które się nie kończy - zależałoby to od tego, gdzie wywołujesz ten kod i czy masz sposób na pobranie kontekst z innej, niedokończonej czynności.
DiscDev
8
Stukrotne dzięki! U mnie awaria (z komunikatem o błędzie powyżej) wystąpiłaby, gdy nacisnąłem przycisk Wstecz, zanim pojawiło się okno dialogowe. Zatem kod będzie kontynuowany i próbował go pokazać, mimo że wykonywałem inną czynność. Ale to zatrzymało awarię iz łatwością przeszedłem do nowej aktywności!
Azurespot
1
Jest wiele podobnych pytań, ale w żadnym z nich nie zostało to zasugerowane. Osobiście było to jedyne poprawne rozwiązanie dla mojego scenariusza.
fillobotto
jaka jest wersja kotlin do tego? czy isFinishing () wystarczy?
Alok Rajasukumaran
12

Zmierzyłem się z tym samym problemem i użyłem kodu zaproponowanego przez DiscDev powyżej z niewielkimi zmianami w następujący sposób:

if (!MainActivity.this.isFinishing()){
    alertDialog.show();
}
Hamza Polat
źródło
Zmieniłem się, ponieważ średnia kontekstu (Activity) była MainActivity.To dla mojego przypadku. Masz również rację co do linku do profilu użytkownika, ale pomyślałem, że możesz go znaleźć powyżej.
Hamza Polat
4

jeśli okno dialogowe rozwiązuje ten problem z powodu wątku, powinieneś to zrobić w wątku UI w ten sposób: -

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

            }
        });
Rahul Rao
źródło
2

Ten błąd występuje, gdy jest wyświetlane okno dialogowe kontekstu, który już nie istnieje.

Przed wywołaniem .show()sprawdź, czy aktywność / kontekst się nie kończy

if (!(context instanceof Activity && ((Activity) context).isFinishing())) {
    alert.show();
}
akhilesh0707
źródło
1

Napotkałem ten błąd, gdy miałem countDownTimerw mojej aplikacji. Miał metodę wywołującą GameOver w mojej aplikacji jako

public void onFinish() {
     GameOver();
}

ale w rzeczywistości gra mogła się zakończyć przed upływem czasu z powodu niewłaściwego kliknięcia użytkownika (była to gra polegająca na klikaniu). Kiedy więc patrzyłem na okno dialogowe Koniec gry po np. 20 sekundach, zapomniałem anulować, countDownTimerwięc po upływie tego czasu okno dialogowe pojawiło się ponownie. Lub z jakiegoś powodu zawiesił się z powyższym błędem.

erdomester
źródło
1

Rozwiązanie tego jest dość proste. Po prostu sprawdź, czy działanie przechodzi przez fazę kończenia przed wyświetleniem okna dialogowego:

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

zobacz więcej tutaj

Diego Venâncio
źródło
1

W moim przypadku problem polegał na tym, że Contextbyło to słabe odniesienie w klasie, która się rozszerza Handler. Potem przechodziłem Messenger, co otacza przewodnika, przez a Intentdo Service. Robiłem to za każdym razem, gdy aktywność pojawiała się na ekranie w onResume()metodzie.

Jak więc rozumiesz, Messenger został serializowany wraz z jego polami (w tym kontekstem), ponieważ jest to jedyny sposób na przekazywanie obiektów za pomocą Intent- w celu ich serializacji. W tym momencie, gdy Messenger został przekazany do usługi, sama aktywność nadal nie była gotowa do wyświetlania okien dialogowych, ponieważ jest w innym stanie (mówiąc o onResume (), który jest całkowicie inny niż wtedy, gdy aktywność jest już na ekranie). Więc kiedy messenger został zdeserializowany, kontekst nadal był w stanie wznowienia, podczas gdy aktywność faktycznie była już na ekranie. Ponadto deserializacja przydziela pamięć dla nowego obiektu, który jest zupełnie inny niż oryginalny.

Rozwiązaniem jest po prostu wiązanie się z usługą za każdym razem, gdy jej potrzebujesz, zwracanie segregatora, który ma metodę taką jak „setMessenger (komunikator Messenger)” i wywoływanie go, gdy jesteś powiązany z usługą.

Turkhan Badalov
źródło
1

Rozwiązuję ten problem za pomocą WeakReference<Activity> jako kontekstu. Katastrofa nigdy się nie powtórzyła. Oto przykładowy kod w Kotlinie:

Klasa menedżera dialogów:

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

}

I pokazujesz takie okno dialogowe:

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

Jeśli chcesz być super-duper zabezpieczony przed awariami. Zamiast builder.create().show()używać:

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

Oto safeShowmetoda:

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

Jest to podobna metoda, której możesz użyć do bezpiecznego zamknięcia okna dialogowego:

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
źródło
0

co powiesz na utworzenie nowej instancji tego okna dialogowego, które chcesz wywołać? Właściwie właśnie napotkałem ten sam problem i to właśnie robię. więc zamiast:

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

co powiesz na to?

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

więc zamiast po prostu sprawdzać, czy wyświetlanie okna dialogowego jest bezpieczne, czy też nie, myślę, że znacznie bezpieczniej jest, jeśli po prostu utworzymy nową instancję, aby wyświetlić okno dialogowe.

Podobnie jak ja, w moim przypadku próbowałem utworzyć jedną instancję (z Fragment onCreate ) i wywołać instancję tych okien dialogowych w innej zawartości adapterList, co spowoduje wyświetlenie błędu „czy Twoja aktywność jest uruchomiona” - błąd . Pomyślałem, że to dlatego, że po prostu tworzę jedną instancję (z onCreate), a następnie jest ona niszczona, więc kiedy próbowałem wywołać ją z innej listy adapterów , wywołuję okno dialogowe ze starej instancji.

Nie jestem pewien, czy moje rozwiązanie jest przyjazne dla pamięci, czy nie, ponieważ nie próbowałem go profilować, ale działa (cóż, jest bezpieczne, jeśli nie utworzysz zbyt wielu instancji)

Dan
źródło