Прежде чем что-то изобретать, я, конечно, погуглил (автор этого поста уже после выложил своё решение).
Дело в том, что в UI потоке нельзя ждать, а Activity предоставляет событийную модель работы с диалогами: по закрытию диалога вызывается общий обработчик с идентификатором диалога. Такой подход мне показался не очень удобным и я решил сделать все по-своему.
Раз нельзя заставлять ждать UI поток — создадим другой поток и заставим ждать его, а раз так, то мне понадобится механизм синхронизации потоков. Для этой цели я написал элементарный мьютекс. Метод lock() не возвращает управление до тех пор пока не будет вызван unlock().
Далее я создал класс для работы с диалогом и включил в него мьютекс. Класс инициализирует диалог и хранит реакцию пользователя после завершения работы с диалогом. Я опустил реализиацию некоторых методов класса, чтобы сфокусироваться только на важной части реализации.
Всем кнопкам назначены обработчики(1), которые сохраняют возвращаемое значение в переменной, а после этого закрывают диалог. Обработчик закрытия диалога(2) освобождает мьютекс. Интерфейсная функция(3) ожидает освобождения мьютекса.
Ну и, собственно, то, чего я изначально хотел: метод, который ждет реакции пользователя(2). Он должен работать в отдельном потоке.
Сначала мы создаем SyncDialog в UI потоке(1), затем, в фоновом потоке, отправляем на исполнение в UI поток инициализацию и запуск диалога(3), ждем(4) и обрабатываем результат(5).
Запустить processDialogs() можно при помощи AsyncTask
Для последовательного отображения диалоговых окон используя событийный подход, мне в голову пришла только идея создать список из структур, хранящих данные для отображения диалога, и, по закрытию диалога, инициализировать новый диалог следующей в списке структурой. Этот метод мне показался совсем неудобным. Надеюсь, статья окажется полезной тем кто искал решение подобной проблемы.
Спасибо за внимание.
Полный исходный текст примера можно посмотреть тут.
Дело в том, что в UI потоке нельзя ждать, а Activity предоставляет событийную модель работы с диалогами: по закрытию диалога вызывается общий обработчик с идентификатором диалога. Такой подход мне показался не очень удобным и я решил сделать все по-своему.
Раз нельзя заставлять ждать UI поток — создадим другой поток и заставим ждать его, а раз так, то мне понадобится механизм синхронизации потоков. Для этой цели я написал элементарный мьютекс. Метод lock() не возвращает управление до тех пор пока не будет вызван unlock().
public class Mutex { public synchronized void lock() throws InterruptedException { this.wait(); } public synchronized void unlock() { this.notify(); } }
Далее я создал класс для работы с диалогом и включил в него мьютекс. Класс инициализирует диалог и хранит реакцию пользователя после завершения работы с диалогом. Я опустил реализиацию некоторых методов класса, чтобы сфокусироваться только на важной части реализации.
Всем кнопкам назначены обработчики(1), которые сохраняют возвращаемое значение в переменной, а после этого закрывают диалог. Обработчик закрытия диалога(2) освобождает мьютекс. Интерфейсная функция(3) ожидает освобождения мьютекса.
public class SyncDialog { private Dialog mDialog; private Mutex mMutex; private int mResult; private Button mYesButton; private Button mNoButton; private Button mCancelButton; public SyncDialog(Context context) { mMutex = new Mutex(); mDialog = new Dialog(context); mDialog.setContentView(R.layout.dialog); findViews(); mYesButton.setOnClickListener(new OnClickListener() { // (1) public void onClick(View v) { mResult = YES_RESULT; mDialog.dismiss(); } }); mNoButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { mResult = NO_RESULT; mDialog.dismiss(); } }); mCancelButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { mResult = CANCEL_RESULT; mDialog.cancel(); } }); mDialog.setOnDismissListener(new OnDismissListener() { // (2) public void onDismiss(DialogInterface dialog) { mMutex.unlock(); } }); } public void waitForResponse() throws InterruptedException { // (3) mMutex.lock(); } }
Ну и, собственно, то, чего я изначально хотел: метод, который ждет реакции пользователя(2). Он должен работать в отдельном потоке.
Сначала мы создаем SyncDialog в UI потоке(1), затем, в фоновом потоке, отправляем на исполнение в UI поток инициализацию и запуск диалога(3), ждем(4) и обрабатываем результат(5).
public class MyActivity extends Activity { private SyncDialog syncDialog; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); syncDialog = new SyncDialog(this); // (1) } public void processDialogs(String... questions) { // (2) for (final String question : questions) { runOnUiThread(new Runnable() { // (3) public void run() { syncDialog.setText(question); syncDialog.show(); } }); int response; try { syncDialog.waitForResponse(); // (4) response = syncDialog.getResult(); } catch (InterruptedException e) { response = SyncDialog.CANCEL_RESULT; } if (response == SyncDialog.YES_RESULT) { // (5) Log.i(question, "Yes"); }else if (response == SyncDialog.NO_RESULT) { Log.i(question, "No"); }else break; } } }
Запустить processDialogs() можно при помощи AsyncTask
public class DialogsTask extends AsyncTask<String, Void, Void> { @Override protected Void doInBackground(String... params) { processDialogs(params); return null; } }
Для последовательного отображения диалоговых окон используя событийный подход, мне в голову пришла только идея создать список из структур, хранящих данные для отображения диалога, и, по закрытию диалога, инициализировать новый диалог следующей в списке структурой. Этот метод мне показался совсем неудобным. Надеюсь, статья окажется полезной тем кто искал решение подобной проблемы.
Спасибо за внимание.
Полный исходный текст примера можно посмотреть тут.
