Почему AlertDialog.Builder(Context context) принимает только Activity в качестве параметра?

В процессе постоянного обучения (на этот раз с диалоговыми окнами) я обнаружил, что это работает:

  AlertDialog.Builder builder = new AlertDialog.Builder(this);

Пока следующее не работает (сбой во время выполнения с WindowManager$BadTokenException):

  AlertDialog.Builder builder = new AlertDialog.Builder(this.getApplicationContext());

Я не понимаю почему, потому что конструктор для AlertDialog.Builder определен так, чтобы принимать Context в качестве параметра, а не Activity:

общедоступный AlertDialog.Builder (контекстный контекст)

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

Что мне не хватает?


person an00b    schedule 25.03.2011    source источник
comment
Это верно и для других диалогов. Хороший вопрос, +1   -  person bigstones    schedule 25.03.2011
comment
@bigstones Я обнаружил еще одну ветку, посвященную аналогичной проблеме, но без объяснения причин: stackoverflow.com/questions/3968170/   -  person an00b    schedule 25.03.2011
comment
Я предполагаю, что Builder не просто запрашивает действие, потому что это не позволит будущим API иметь другие виды контекстов, которые могут отображать диалог.   -  person bigstones    schedule 25.03.2011
comment
@bigstones правильная идея, за исключением того, что это не только будущее, но и настоящее. AlertDialog.Builder может использоваться ListActivity, Service, ... и любым наследованием от Context именно потому, что его аргумент предназначен для абстрактного контекста.   -  person DJC    schedule 29.03.2011
comment
@DJC на самом деле вы не можете отображать диалоги непосредственно из классов, наследуемых от Context, кроме Activity. mail-archive.com/[email protected]/   -  person bigstones    schedule 29.03.2011
comment
@bigstones ааа, я понимаю, что делал это только с Activity и его подклассами, и нет смысла использовать его из Сервиса. В любом случае, я сделал свою работу, передав активность в общие классы в качестве аргумента.   -  person DJC    schedule 30.03.2011
comment
Как ни странно, Google предоставляет пример, который показывает, что вы можете использовать getApplicationContext(). В самом низу этой страницы (developer.android.com/guide/topics/ ui/dialogs.html), они предоставляют пример AlertDialog, который включает следующее: Context mContext = getApplicationContext(); ... builder = новый AlertDialog.Builder(mContext); Технически это ДЕЙСТВИТЕЛЬНО позволяет мне сделать это, но мое приложение взрывается, когда я пытаюсь отобразить диалоговое окно. Хм...   -  person Robert Nekic    schedule 30.04.2011


Ответы (2)


Действие наследует контекст. AlertDialog.Builder указывает аргумент Context, потому что затем он может использоваться ЛЮБЫМ классом, который является подклассом Context, включая Activity, ListActivity, Service,... (за этим стоит общая идиома кодирования — вы можете узнать больше об этом прочитав пункт I8 (об интерфейсах и абстрактных классах) в фантастической книге Джошуа Блоха «Эффективная Java»).

getApplicationContext() возвращает контекст для вашего приложения, который в основном совпадает с контекстом ваших действий, и «в основном» это то, что вас сбивает. Детали неясны, но это широко распространенная проблема, и типичным ответом является использование контекста, который будет выводить предупреждение на экран. Обратите внимание, что это не тот, который возвращает getApplicationContext().

Теперь, если вы похожи на меня, вы можете сказать: «Но я работаю в классе, который не наследуется от Activity — вот почему я хочу использовать для этого getApplicationContext() в первую очередь — ну да!» Я на самом деле не говорю так грубо ;p .. Дело в том, что я тоже был здесь. Я исправил это следующим образом: 1) спросите себя: «Есть ли у меня мой код UI AlertDialog в классе без активности, потому что я хочу поделиться им между действиями… или даже между ListActivities, Services,…?». Если нет, хммм... у вас действительно есть вызовы пользовательского интерфейса AlertDialog в коде, который, как вы не можете гарантировать, будет иметь доступ к пользовательскому интерфейсу (и, следовательно, к контексту)? Если это так, пересмотрите свой дизайн.

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

myClass(Context theContext, ...) { ... }

Каждое действие, услуга и т. д. затем вызывает такие вызовы:

myClass(this, ...);

Выглядит знакомо?

Будьте осторожны! что, если вы используете общий код, вы должны учитывать возможность параллельного поступления различных вызовов в ваш общий код со всеми многочисленными последствиями. Это вне нашей компетенции здесь ...

Развлекайся :)

person DJC    schedule 29.03.2011
comment
Спасибо за Ваш ответ. К сожалению, я до сих пор не понимаю, почему AlertDialog.Builder(Context context) принимает только Activity в качестве параметра. Вы говорите, что контекст this отличается от this.getApplicationContext()? Пожалуйста, объясни. - person an00b; 31.03.2011
comment
да, кажется, что контекст Activity является расширенным набором контекста приложения — Activity специализируется на контексте приложения, добавляя такие вещи, как те, которые используются AlertDialog. К вашему сведению, это мое понимание, основанное на чтении других выводов по этому же вопросу. В чем я больше всего уверен, так это в том, что передача Activity в мой код сработала. - person DJC; 01.04.2011
comment
Однозначный ответ по-прежнему проблематичен, потому что два действия могут не обязательно использовать только один и тот же класс, они могут захотеть использовать один и тот же экземпляр этого класса. Если это так, то вы не можете позволить обеим действиям создавать общий класс со своим собственным контекстом. Это действительно ужасный выбор дизайна для Android. С какой стати вы когда-либо хотели создавать AlertDialogs, а не отображать их всплывающими окнами над всеми действиями? iOS позволяет создавать UIAlertViews из любого места. Android тоже должен. - person Nate; 25.08.2011

AlertDialog является подклассом Диалоговое окно со связанным Window, с которым связан Параметры макета. Одним из этих параметров является тип окна. Тип по умолчанию — TYPE_APPLICATION_ATTACHED_DIALOG, для которого требуется родительское окно. .

WindowManager, связанный с действием, настроен на использование окна действия в качестве родительское окно. WindowManager, связанный с приложением, не имеет связанного родительского окна.

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

person j__m    schedule 24.09.2016