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