"android.view.WindowManager$BadTokenException: невозможно добавить окно" на buider.show()

С моей главной 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();
}

11 ответов

Решение
android.view.WindowManager$BadTokenException: Unable to add window"

Проблема:

Это исключение возникает, когда приложение пытается уведомить пользователя из фонового потока (AsyncTask), открыв диалоговое окно.

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

Причина:

Причина этого исключения заключается в том, что, как говорится в сообщении об исключении, действие завершено, но вы пытаетесь отобразить диалоговое окно с контекстом завершенного действия. Поскольку в диалоговом окне нет окна для отображения времени выполнения Android, возникает это исключение.

Решение:

использование isFinishing() метод, который вызывается Android для проверки того, находится ли это действие в процессе завершения: будь то явный вызов finish () или очистка действия, выполненная 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 создает специальный вид токена окна, называемый токеном окна приложения, который однозначно идентифицирует окно контейнера верхнего уровня приложения. Диспетчер активности передает этот токен как приложению, так и диспетчеру окон, и приложение отправляет токен диспетчеру окон каждый раз, когда он хочет добавить новое окно на экран. Это обеспечивает безопасное взаимодействие между приложением и диспетчером окон (делая невозможным добавление окон поверх других приложений), а также упрощает для диспетчера операций прямые запросы к диспетчеру окон.

У меня был диалог, показывающий функцию:

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

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

if(!isFinishing())
    showDialog();

Возможная причина - контекст диалогового окна предупреждения. Вы можете закончить это действие, поэтому оно пытается открыть в этом контексте, но которое уже закрыто. Попробуйте изменить контекст этого диалога на первое действие, потому что оно не будет завершено до конца.

например

а не это.

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

попробуй использовать

AlertDialog alertDialog = new AlertDialog.Builder(FirstActivity.getInstance()).create();
  • Сначала вы не можете расширить AsyncTask без переопределения doInBackground
  • вторая попытка создать AlterDailog из компоновщика, затем вызвать 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();
    }
    

Я создаю диалог в 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();

Я пытаюсь это решить.

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

В моем случае я провел рефакторинг кода и вынес создание диалога в отдельный класс. Я передал только нажатый View, потому что View уже содержит объект контекста. Это привело к тому же сообщению об ошибке, хотя все они выполнялись в MainThread.

Затем я переключился на передачу Activity и использовал его контекст при создании диалога -> Теперь все работает.

    fun showDialogToDeletePhoto(baseActivity: BaseActivity, clickedParent: View, deletePhotoClickedListener: DeletePhotoClickedListener) {
        val dialog = AlertDialog.Builder(baseActivity) // <-- here
   .setTitle(baseActivity.getString(R.string.alert_delete_picture_dialog_title))
...
}

Я не могу правильно отформатировать фрагмент кода, извините:(

Я прочитал этот и целый ряд других ответов и не смог решить эту проблему для своего случая; в конце концов я обнаружил, что проблема в моем случае заключалась в том, что при создании Builder я использовал контекст, полученный через GetApplicationContext, вместо активности.

Я знаю, что здесь это не тот случай, но поскольку это первая и лучшая ветка по этому исключению, я размещаю здесь на случай, если это будет полезно другим.

Я получил эту ошибку, но мой исходил из тостов, а не из диалога.

В моем макете есть активность и фрагменты. Код для тоста был в классе Activity. Фрагменты загружаются перед Activity.

Я думаю, что код Toast был запущен до завершения инициализации Context/Activity. Я думаю это был getApplicationContext() в команде Toast.makeText(getApplicationContext(), "onMenutItemActionCollapse called", Toast.LENGTH_SHORT).show();

Попробуй это:

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

}
}

С этой идеей глобальных переменных я сохранил экземпляр 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. Open MainActivity
  2. Начать тему.
  3. Открыть диалог из потока -> работа.
  4. Нажмите кнопку "Назад" (будет вызвано onCreate и сначала удалите MainActivity)
  5. Новый MainActivity начнется. (и сохранить его экземпляр для глобальных)
  6. Открыть диалог из первой темы -> он откроется и заработает.

:)

Другие вопросы по тегам