Pytanie Dlaczego wznowienie działania w Androidzie powoduje, że BadTokenException?


Ludzie - czy ktoś może wyjaśnić ten stos? Zauważ, że mój kod nigdzie się nie znajduje. Jeśli korzystasz z Google'a w przypadku któregokolwiek z tych wyjątków, każdy, kto napotkał ten problem, próbował utworzyć okna dialogowe po zakończeniu działania, co nie ma miejsca w tym przypadku. To tylko proste wznowienie działania. Dostrzegam ten wyjątek zgłaszany przez klientów w tej dziedzinie dość często i chciałbym go poprawić, jeśli to możliwe.

android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@405177d8 is not valid; is your activity running?
at android.view.ViewRoot.setView(ViewRoot.java:527)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:177)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)
at android.view.Window$LocalWindowManager.addView(Window.java:424)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2268)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1721)
at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:2955)
at android.app.ActivityThread.access$1600(ActivityThread.java:124)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:972)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:3806)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
at dalvik.system.NativeStart.main(Native Method)

Aktualizacja:

Oto, w jaki sposób mogę zdalnie odzyskać ten stos. Najpierw dodaję uncaughtExceptionHandler na początku mojej działalności w onCreate:

try {
  File crashLogDirectory = new File(Environment.getExternalStorageDirectory().getCanonicalPath() + Constants.CrashLogDirectory);
  crashLogDirectory.mkdirs();

  Thread.setDefaultUncaughtExceptionHandler(new RemoteUploadExceptionHandler(this, crashLogDirectory.getCanonicalPath()));
} catch (Exception e) {
  if (MyActivity.WARN) Log.e(MyActivity.TAG, "Exception setting up exception handler! " + e.toString());
}

W mojej klasie RemoteUploadExceptionHandler mam następujący kod:

public void uncaughtException(Thread t, Throwable e) {        
  String timestamp = Calendar.getInstance().getTime().toGMTString();
  String filename = timestamp + ".stacktrace";
  final Writer result = new StringWriter();
  final PrintWriter printWriter = new PrintWriter(result);
  e.printStackTrace(printWriter);
  String stacktrace = result.toString();
  printWriter.close();
  sendToServer(stacktrace, filename);
  defaultUEH.uncaughtException(t, e);
}


private void sendToServer(String stacktrace, String filename) {
    DefaultHttpClient httpClient = new DefaultHttpClient();
    HttpPost httpPost = new HttpPost(Constants.RemoteUploadUrl);
    List<NameValuePair> nvps = new ArrayList<NameValuePair>();
    nvps.add(new BasicNameValuePair("filename", filename));
    nvps.add(new BasicNameValuePair("stacktrace", stacktrace));
    nvps.add(new BasicNameValuePair("platform_version", platformVersion));
    nvps.add(new BasicNameValuePair("device_id", deviceId));

    nvps.add(new BasicNameValuePair("build_device", Build.DEVICE));
    nvps.add(new BasicNameValuePair("build_brand", Build.BRAND));
    nvps.add(new BasicNameValuePair("build_product", Build.PRODUCT));
    nvps.add(new BasicNameValuePair("build_manufacturer", Build.MANUFACTURER));
    nvps.add(new BasicNameValuePair("build_model", Build.MODEL));
    nvps.add(new BasicNameValuePair("build_version", String.format("%d",Build.VERSION.SDK_INT)));

    try {
        httpPost.setEntity(
                new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
        httpClient.execute(httpPost);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

To jest kod, który przesyła mi wiele stosów na godzinę, jak ten, który pokazałem powyżej.

Co więcej, jeśli spojrzysz na kod ActivityThread przez wyszukiwanie kodu Google możesz zobaczyć tę kontrolę przed wywołaniem addView:

if (r.window == null && !a.mFinished && willBeVisible) {

Tak więc działanie nie zostało zakończone i jako takie powinno nadal być prawidłowe.

Ponadto numery linii nie pasują do tego, co widać w kodzie źródłowym Google. Sprawdzić plik ActivityThread.java w źródle 2.3.3. Linia 2268 znajduje się w prywatnej metodzie createThumbnailBitmap. Wersja kompilacji przesłana przez klienta powodującego awarię to 10, co oznacza, że ​​SDK_INT wynosi 10, a więc 2.3.3.


12
2018-05-02 06:54


pochodzenie


Opublikuj kod działania, który zgłasza wyjątek - Lukas Knuth
To jest jedyny stos, jaki widzę, więc w tym przypadku nie wiem, która aktywność to powoduje. Dostaję ten stos powyżej, dodając nieobsługiwaną operację odrywania, która umożliwia zdalne przesyłanie śledzenia stosu do moich serwerów. - esilver
Więc opublikuj kod na swoim Handlerze i jak go dodać ... - Lukas Knuth
K, zaktualizował oryginalne pytanie z kodem na temat działania handler'a. - esilver
Czy rozwiązałeś ten problem? Mam dokładnie ten sam problem i nie znalazłem dla niego poprawki. Urządzeniem jest HTC One S z systemem Android 4.0.3. - Blehi


Odpowiedzi:


Mogłem stale odtwarzać ten problem, gdy w Application.onCreate () jest czasochłonna operacja. W takim przypadku, jeśli naciśniesz ikonę aplikacji z programu uruchamiającego, ale szybko naciśnij przycisk domowy i uruchom inne aplikacje, w końcu dostanę tę awarię.

Zmiana w mojej aplikacji, która umożliwia tę awarię, jest możliwa android:noHistory="true" w deklaracji aktywności w AndroidManifest.xml.

Wygląda na to, że Android traktuje działania z historią backstacków i bez różnicy dla celów tokena okna.


5
2017-07-15 16:03



"czasochłonna operacja uruchomiona w Application.onCreate" Nigdy nie powinieneś mieć czasochłonnej operacji na głównym wątku. - Jared Burrows
Wiem, że tylko pomaga w powtarzaniu problemu i zrozumieniu głównej przyczyny. Nawet jeśli nie mam czasochłonnej operacji, jest to sytuacja wyścigowa, a ułamek naszych użytkowników nadal jest możliwy. - Tapemaster
Istnieje chiński blog wyjaśniający, dlaczego tak się dzieje blog.desmondyao.com/android-bad-window-token . Aktywność z android:noHistory="true" zostanie zakończone, jeśli aplikacja i działanie onCreate będą kosztować dużo czasu. Rozwiązania zmniejszają czasochłonność w procesie onCreate lub po prostu usuń android:noHistory="true" - Carlos Parker


Istnieje wiele raportów o tym samym wyjątku. Wszystkie wskazują na widok, który używa niewłaściwego Context.

Zobacz przykłady poniżej i spróbuj znaleźć w swojej Działalności coś podobnego:

http://groups.google.com/group/android-developers/browse_thread/thread/7a648edddccf6f7d

http://www.anddev.org/view-layout-resource-problems-f27/how-to-fix-this-windowmanager-badtokenexception-t16555.html

Android: ProgressDialog.show () ulega awarii z getApplicationContext

Android 1.6: "android.view.WindowManager $ BadTokenException: Nie można dodać okna - token null nie jest dla aplikacji"

Sprawdź na setView(...) metoda w ViewRoot.java kod. Może pomóc Ci dowiedzieć się: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.2_r1.1/android/view/ViewRoot.java#ViewRoot.setView%28android.view.View % 2Candroid.view.WindowManager.LayoutParams% 2Candroid.view.View% 29

Zwłaszcza linie:

case WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN:
  throw new WindowManagerImpl.BadTokenException(
    "Unable to add window -- token " + attrs.token
    + " is not valid; is your activity running?");

2
2018-05-03 20:03



Dzięki za informację. Niestety te wszystkie wydają się być związane z tym samym błędem: wywoływanie getApplicationContext () i przekazywanie go jako kontekstu do okna dialogowego Dialog.show. To nie wydaje się być moim problemem. Co więcej, fakt, że żaden z moich kodów aplikacji nie jest na stosie, jest mylący; Spodziewam się zobaczyć mój kod na stosie. - esilver
Tak, ten brak w stosie uniemożliwia nam wyciągnięcie wniosku. Patrzę na ostatnią linię w stosie przed wyjątkiem: at android.view.ViewRoot.setView(ViewRoot.java:527) co sugeruje mi metodę onDraw () lub onStart () próbującą nadmuchać widok. - Aleadam
Racja, tak skutecznie linia 497 ViewRoot (bit.ly/itqRiP) zwraca WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN lub WindowManagerImpl.ADD_BAD_APP_TOKEN. Właśnie sprawdziłem i wszystkie moje metody onStart to wywołanie FlurryAgent.onStartSession (this, Constants.FlurryClientId). Nie mam żadnych metod onDraw. Z pewnością możliwe jest rzucanie podmuchów; jednak po prostu wykonałem test, w którym zrobiłem wyjątek DivideByZero w mojej metodzie onStart i fakt ten pojawia się na stosie. Dlatego nie sądzę, by w tym przypadku był to błąd. - esilver
Jedna ciekawa rzecz: nie powiodło mi się, że próbowałem zafałszować dokładnie ten sam stos; w szczególności nie widzę handleRelaunchActivity na stosie, gdy umieszczam widok w tle, a następnie odtwarzam go, klikając ikonę aplikacji. Patrząc na ten wątek: stackoverflow.com/questions/4466529/... Zastanawiam się, czy w rzeczywistości może być jakiś niespokrewniony wyjątek wcześniej, który uruchamia "ponowne uruchomienie", który sam się nie powiedzie? - esilver
Słowo kluczowe to android.app.ActivityThread.handleResumeActivity. Nie o żadnym widoku ani o dialogu. To działanie nie może się przyczepić do okna. Tak więc ta odpowiedź nie jest związana z pytaniem. - Kimi Chiu


Miałem ten sam problem, ten sam błąd bez śladu stosu.

Korzystałem z wielu pojedynczych klas, które zawierały zmienną wskazującą kontekst aktywności. Po przepisaniu kodu w celu wyeliminowania takich odniesień do kontekstu problem zniknął.

Więc nawet jeśli nie ma problemu z dialogiem, może się okazać, że odwołujesz się do kontekstu w niewłaściwy sposób w innym miejscu kodu.


0
2018-06-20 15:35





Ta katastrofa przez długi czas mnie niepokoiła, ale w końcu ją naprawiłem. po prostu dodaj następujący kod do swojej klasy aktywności

private boolean mDestroyed = false;

public final boolean isActivityDestroyed() {
    return mDestroyed;
}

@Override
protected void onPostResume() {
    super.onPostResume();
    if(isFinishing()){
        finish();
    }
}

@Override
public boolean isFinishing() {
    return super.isFinishing() || isActivityDestroyed();
}

@Override
protected void onDestroy() {
    super.onDestroy();
    mDestroyed = true;
}

Działa to dobrze dla mnie, ale nie znam podstawowej przyczyny tej awarii. Po przeczytaniu ActivityThread.java i ViewRootImpl.java w AOSP będziesz wyraźnie widoczny.


-1
2017-08-23 03:01



Jak powiedziano w innych odpowiedziach, przypuszczam, że trzymasz odniesienie do kontekstu w jakimś miejscu. - anti_gone