"Android.view.WindowManager $ BadTokenException: Не вдається додати вікно" на buider.show ()


118

З мого основного activity, мені потрібно зателефонувати до внутрішнього класу, і в методі в межах класу мені потрібно показати AlertDialog. Відхиливши її, натиснувши кнопку "ОК", переслати в Google Play для придбання.

У більшості випадків справи прекрасно працюють, але для кількох користувачів він виходить з ладу, builder.show()і я бачу, що "android.view.WindowManager$BadTokenException:неможливо додати вікно "з журналу аварій. Зверніть увагу.

Мій код приблизно такий:

public class classname1 extends Activity{

  public void onCreate(Bundle savedInstanceState) {
    this.requestWindowFeature(Window.FEATURE_NO_TITLE);
    super.onCreate(savedInstanceState);
    setContentView(R.layout.<view>); 

    //call the <className1> class to execute
  }

  private class classNamename2 extends AsyncTask<String, Void, String>{

    protected String doInBackground(String... params) {}

    protected void onPostExecute(String result){
      if(page.contains("error")) 
      {
        AlertDialog.Builder builder = new AlertDialog.Builder(classname1.this);
        builder.setCancelable(true);
        builder.setMessage("");
        builder.setInverseBackgroundForced(true);
        builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
          public void onClick(DialogInterface dialog, int whichButton){
            dialog.dismiss();
            if(!<condition>)
            {
              try
              {
                String pl = ""; 

                mHelper.<flow>(<class>.this, SKU, RC_REQUEST, 
                  <listener>, pl);
              }

              catch(Exception e)
              {
                e.printStackTrace();
              }
            }  
          }
        });

        builder.show();
      }
    }
  }
}

Я також бачив помилку в іншому попередженні, де я не пересилаю жодне інше activity. Це просто так:

AlertDialog.Builder builder = new AlertDialog.Builder(classname1.this);
    builder.setCancelable(true);

    //if successful
    builder.setMessage(" ");
    builder.setInverseBackgroundForced(true);
    builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int whichButton){
            // dialog.dismiss();
                   }
    });
    builder.show();
}

2
Якщо це ваш повний код, чи справді вам потрібен AsyncTask?
Shobhit Puri

Це не повний код, це досить великий код, тому я лише додав туди частину, де я бачу проблему із звіту про аварійну
ситуацію

так добре. Зазвичай ви можете просто опублікувати ім'я функції та прокоментувати, що ви робите там багато речей (як це робили зараз). Його легше зрозуміти. :).
Shobhit Puri

Ви десь переходите до якоїсь іншої діяльності з цієї діяльності?
Shobhit Puri

1
Ви написали - це коментарі //send to some other activity. Я думаю, якщо ви прокоментуєте ту частину, де ви збираєтесь до нової діяльності, ця помилка піде назовні. Здається, помилка трапляється через те, що діалогове вікно до завершення повністю закривається, починається ваша нова активність. У вікні onPostExecute()з'являється діалогове вікно попередження, і ви даєте контекст loginдіяльності. Але ви переходите до іншої діяльності, тому контекст стає неправильним. Отже, ви отримуєте цю помилку! Дивіться stackoverflow.com/questions/15104677/… подібне запитання.
Shobhit Puri

Відповіді:


264
android.view.WindowManager$BadTokenException: Unable to add window"

Проблема:

Цей виняток виникає, коли програма намагається повідомити користувача з фонового потоку (AsyncTask), відкривши діалоговий вікно.

Якщо ви намагаєтесь змінити інтерфейс користувача з фонового потоку (як правило, з onPostExecute () AsyncTask), і якщо активність переходить у фінішну стадію, тобто) явно викликаючи закінчення (), натискаючи користувачем кнопку додому або назад або активуйте очищення, зроблене Android, ви отримати цю помилку.

Причина:

Причиною цього винятку є те, що, як йдеться у повідомленні про виключення, активність закінчена, але ви намагаєтесь відобразити діалогове вікно з контекстом завершеної діяльності. Оскільки для діалогового вікна для відображення андроїдського періоду виконання немає вікна, це виключає.

Рішення:

Скористайтеся isFinishing()методом, який викликається Android, щоб перевірити, чи ця діяльність закінчується: будь то явний виклик закінчення () чи очищення діяльності, здійснений Android. За допомогою цього методу дуже легко уникнути відкриття діалогового вікна з фонового потоку, коли діяльність закінчується.

Також підтримуйте weak referenceактивність (і не є надійною посиланням, щоб активність можна було знищити, коли вона не потрібна) та перевірте, чи активність не закінчується, перш ніж виконувати будь-який інтерфейс, використовуючи цей довідник про діяльність (тобто показуючи діалогове вікно).

напр .

private class chkSubscription extends AsyncTask<String, Void, String>{

  private final WeakReference<login> loginActivityWeakRef;

  public chkSubscription (login loginActivity) {
    super();
    this.loginActivityWeakRef= new WeakReference<login >(loginActivity)
  }

  protected String doInBackground(String... params) {
    //web service call
  }

  protected void onPostExecute(String result) {
    if(page.contains("error")) //when not subscribed
    {
      if (loginActivityWeakRef.get() != null && !loginActivityWeakRef.get().isFinishing()) {
        AlertDialog.Builder builder = new AlertDialog.Builder(login.this);
        builder.setCancelable(true);
        builder.setMessage(sucObject);
        builder.setInverseBackgroundForced(true);

        builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
          public void onClick(DialogInterface dialog, int whichButton){
            dialog.dismiss();
          }
        });

        builder.show();
      }
    }
  }
}

Оновлення:

Токни для вікон:

Як випливає з назви, маркер вікна - це особливий тип маркера Binder, який використовує менеджер вікон для унікальної ідентифікації вікна в системі. Вікна жетонів важливі для безпеки, оскільки вони унеможливлюють надходження шкідливих програм поверх вікон інших програм. Менеджер вікон захищає від цього, вимагаючи, щоб програми передавали маркер вікна своєї програми як частину кожного запиту для додавання чи видалення вікна. Якщо маркери не збігаються, менеджер вікон відхиляє запит і видає BadTokenException . Без маркерів вікон цей необхідний крок ідентифікації був би неможливим, і менеджер вікон не зміг би захиститися від шкідливих програм.

 Реальний сценарій:

Коли програма запускається вперше,  ActivityManagerService  створює особливий вид маркера вікна, який називається маркер вікна програми, який однозначно ідентифікує вікно контейнера верхнього рівня програми. Менеджер активності надає цей маркер і програмі, і менеджеру вікон, а програма надсилає маркер менеджеру вікон кожного разу, коли хоче додати нове вікно на екран. Це забезпечує безпечну взаємодію програми та менеджера вікон (унеможливлюючи додавання вікон поверх інших програм), а також спрощує керівнику діяльності прямі запити до менеджера вікон.


Це має багато сенсу! Також ваше рішення звучить для мене чудово. (у)
MSIslam

Повідомлення 'порожнє остаточне поле loginActivityWeakRef, можливо, не було ініціалізовано' та спробували таким чином: приватне остаточне WeakReference <login> loginActivityWeakRef = new WeakReference <login> (login.this); не впевнений, чи правильно це робити
MSIslam

Також я видалив фінал перед WeakReference <login> loginActivityWeakRef, оскільки він показав помилку в конструкторі.
MSIslam

1
спробуйте використовувати новий chkCubscription (this) .execute (""); замість нового chkCubscription.execute (""); як ви розмістили вище.
Ritesh Gune

2
Страшна помилка !! Я слідую за підручником, і як @PhilRoggenbuck, моя проблема була викликана викликом Toast..Show () безпосередньо перед викликом StartActivity (...). Щоб виправити це, я перемістив тост замість щойно названої діяльності!
Тьеррі

26

У мене було діалогове відображення функції:

void showDialog(){
    new AlertDialog.Builder(MyActivity.this)
    ...
    .show();
}

Я отримував цю помилку, і я просто повинен був перевірити, isFinishing()перш ніж викликати це діалогове вікно, що показує функцію.

if(!isFinishing())
    showDialog();

1
не варто писати if(!MyActivity.this.isFinishing())? Якщо це неправильно в MyActivity
Bibaswann Bandyopadhyay

2
Навіщо Android запускати будь-який код, якщо він уже закінчується? Якщо ми будемо дотримуватися цього рішення, то уявіть, скільки часу нам слід реально використовувати IsFinishing, щоб уникнути подібних проблем.
Девід

@David Я думаю, що в цьому пропущено декілька деталей, наприклад, діалог, який викликається у фоновому потоці, але я повністю згоден з вашою точкою, як це є зараз.
Покинутий кошик

Чудовий момент, чому б чортові мені потрібно перевірити наявність isFinishing!
Chibueze Opata

9

Можлива причина - контекст діалогового вікна попередження. Ви можете закінчити цю діяльність, щоб її намагалися відкрити в цьому контексті, але яка вже закрита. Спробуйте змінити контекст цього діалогового вікна на вашу першу діяльність, оскільки він не буде закінчений до кінця.

напр

а не це.

AlertDialog alertDialog = new AlertDialog.Builder(this).create();

спробуйте використовувати

AlertDialog alertDialog = new AlertDialog.Builder(FirstActivity.getInstance()).create();

3
  • по-перше, ви не можете розширити AsyncTask без заміни doInBackground
  • по-друге, спробуйте створити AlterDailog від будівельника, а потім call show ().

    private boolean visible = false;
    class chkSubscription extends AsyncTask<String, Void, String>
    {
    
        protected void onPostExecute(String result)
        {
            AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
            builder.setCancelable(true);
            builder.setMessage(sucObject);
            builder.setInverseBackgroundForced(true);
            builder.setNeutralButton("Ok", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton)
                {
                    dialog.dismiss();
                }
            });
    
            AlertDialog myAlertDialog = builder.create();
            if(visible) myAlertDialog.show();
        }
    
        @Override
        protected String doInBackground(String... arg0)
        {
            // TODO Auto-generated method stub
            return null;
        }
    }
    
    
    @Override
    protected void onResume()
    {
        // TODO Auto-generated method stub
        super.onResume();
        visible = true;
    }
    
    @Override
    protected void onStop()
    {
        visible = false; 
        super.onStop();
    }

1
Дякую за відповідь. Я фактично використовував метод doInBackground, просто не згадував його тут, оскільки це не пов’язано з оповіщенням. Що стосується додавання builder.create (), то, здається, він працює добре, але не знаю, чи буде він добре працювати для всіх. Як я вже говорив раніше, мій поточний код також працює нормально, але лише кілька разів для кількох користувачів він показує, що не може додати проблему з вікном. Скажіть, будь ласка, що може бути проблемою в моєму кодуванні, що може спричинити це?
MSIslam

у цьому випадку користувач припиняє вашу активність перед тим, як викликати onPostExecute, тому немає вікна для проведення діалогового вікна, і це призводить до збоїв у вашій програмі. додайте прапор до onStop, щоб дізнатися, чи ваша діяльність більше не помітна, тоді не показуйте діалогове вікно.
moh.sukhni

onPostExecute насправді отримує виклик, оскільки builder.show () знаходиться під умовою, коли я перевіряю, чи не підписався користувач на основі результату виклику веб-служби від doInBackground (). Тож якби onPostExecute не був викликаний, він не підходить до рядка builder.show ().
MSIslam

onPostExecute викликається за замовчуванням після doInBackground, ви не можете його викликати, і все, що там буде виконано.
moh.sukhni

1
ваше завдання асинхронізації буде продовжувати працювати після того, як користувач перейде від вашої активності, і це призведе до того, що builder.show () переробить вашу програму, оскільки для вас не існує активності для обробки користувальницького інтерфейсу. тож ваш додаток витягує дані з Інтернету, але ваша діяльність була знищена до отримання даних.
moh.sukhni

1

Я створюю "Діалог" onCreateі використовую його за допомогою showта hide. Для мене першопричиною було не звільнення onBackPressed, що закінчувало Homeдіяльність.

@Override
public void onBackPressed() {
new AlertDialog.Builder(this)
                .setTitle("Really Exit?")
                .setMessage("Are you sure you want to exit?")
                .setNegativeButton(android.R.string.no, null)
                .setPositiveButton(android.R.string.yes,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog,
                                    int which) {
                                Home.this.finish();
                                return;
                            }
                        }).create().show();

Я закінчував домашню діяльність, onBackPressedне закриваючи / відхиляючи діалоги.

Коли я відхилив діалоги, збій зник.

new AlertDialog.Builder(this)
                .setTitle("Really Exit?")
                .setMessage("Are you sure you want to exit?")
                .setNegativeButton(android.R.string.no, null)
                .setPositiveButton(android.R.string.yes,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog,
                                    int which) {
                                networkErrorDialog.dismiss() ;
                                homeLocationErrorDialog.dismiss() ;
                                currentLocationErrorDialog.dismiss() ;
                                Home.this.finish();
                                return;
                            }
                        }).create().show();

0

Я намагаюся це вирішити.

 AlertDialog.Builder builder = new AlertDialog.Builder(
                   this);
            builder.setCancelable(true);
            builder.setTitle("Opss!!");

            builder.setMessage("You Don't have anough coins to withdraw. ");
            builder.setMessage("Please read the Withdraw rules.");
            builder.setInverseBackgroundForced(true);
            builder.setPositiveButton("OK",
                    (dialog, which) -> dialog.dismiss());
            builder.create().show();

-1

Спробуйте це :

    public class <class> extends Activity{

    private AlertDialog.Builder builder;

    public void onCreate(Bundle savedInstanceState) {
                    this.requestWindowFeature(Window.FEATURE_NO_TITLE);
                    super.onCreate(savedInstanceState);

                setContentView(R.layout.<view>); 

                builder = new AlertDialog.Builder(<class>.this);
                builder.setCancelable(true);
                builder.setMessage(<message>);
                builder.setInverseBackgroundForced(true);

        //call the <className> class to execute
}

    private class <className> extends AsyncTask<String, Void, String>{

    protected String doInBackground(String... params) {

    }
    protected void onPostExecute(String result){
        if(page.contains("error")) //when not subscribed
        {   
           if(builder!=null){
                builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton){
                    dialog.dismiss();
                        if(!<condition>)
                        {
                        try
                        {
                        String pl = ""; 

                        mHelper.<flow>(<class>.this, SKU, RC_REQUEST, 
                        <listener>, pl);
                        }

                        catch(Exception e)
                        {
                        e.printStackTrace();
                        }
                    }  
                }
            });

            builder.show();
        }
    }

}
}

-4

за допомогою цієї ідеї змінних глобальних змін я зберегла екземпляр MainActivity в onCreate (); Android глобальна змінна

public class ApplicationController extends Application {

    public static MainActivity this_MainActivity;
}

і відкрити діалогове вікно, як це це спрацювало.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Global Var
    globals = (ApplicationController) this.getApplication();
    globals.this_MainActivity = this;
}

і в потоці я відкриваю такий діалог.

AlertDialog.Builder alert = new AlertDialog.Builder(globals.this_MainActivity);
  1. Відкрийте MainActivity
  2. Почніть нитку.
  3. Відкрити діалог з теми -> робота.
  4. Натисніть кнопку "Назад" (onCreate буде викликано та видаліть першу MainActivity)
  5. Почнеться нова MainActivity. (і збережіть його екземпляр до глобальних)
  6. Відкрийте діалогове вікно з першого потоку -> воно відкриється і запрацює.

:)


4
Ніколи не зберігайте статичні посилання на активність. Це спричинить витік пам’яті
Leandroid
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.