Intereting Posts

Как использовать Runnable.wait () в AsyncTask? Почему AsyncTask не ждет …?

Я использую AsyncTask для запуска фоновой операции. Конечно, переход на другой поток, хотя уже работает в фоновом потоке, не имеет большого смысла вообще, кроме другого потока – поток пользовательского интерфейса. Это то, что я хотел бы: во время выполнения задачи мне нужно «получить доступ» к пользовательскому интерфейсу, например, чтобы показать диалог, чтобы спросить пользователя, как действовать.

  • Выполнить фоновое задание
  • Остановите задачу в какой-то момент, чтобы получить обратную связь с пользователем
  • Переключиться на поток пользовательского интерфейса, чтобы отобразить диалоговое окно и запросить ввод
  • Вернуться к фоновой задаче и продолжить работу

Как это может быть сделано? Я думал, что могу использовать Runnable с myActivity.runOnUiThread(runnable) но это не работает:

 private void copyFiles() { CopyTask copyTask = new CopyTask(this); copyTask.execute(); } // CustomAsyncTask is a AsyncTask subclass that takes a reference to the current // activity as parameter private class CopyTask extends CustomAsyncTask<Void, Void, Void> { private doCopy; @Override protected Boolean doInBackground(Void... params) { // Custom code, eg copy files from A to B and check for conflict for (File file : allFiles) { doCopy = true; if (isConflict(file)) { // Stop current thread and ask for user feedback on UI Thread Runnable uiRunnable = new Runnable() { public void run() { // Pos 1. --> Execute custom code, eg use AlertDialog to ask user if file should be replaced... doCopy = false; synchronized (this) { this.notify(); } } }); synchronized(uiRunnable) { // Execute code on UI thread activity.runOnUiThread(uiRunnable); // Wait until runnable finished try { uiRunnable.wait(); } catch (InterruptedException e ) { e.printStackTrace(); } } } // Pos 2. --> Continue work if (doCopy) copyFromAToB(File); } return null; } } 

Внутри doInBackground() (-> в фоновом потоке) AsyncTask вызывает activity.runOnUiThread(uiRunnable) . Затем uiRunnable.wait() . Что касается документа wait() необходимо сделать следующее:

Заставляет вызывающий поток ждать, пока другой поток вызовет метод notify () или notifyAll () этого объекта.

Таким образом, фоновый поток должен ждать, чтобы продолжить работу, пока this.notify() (== uiRunnable.notifiy() ) вызывается в другом потоке (= поток пользовательского интерфейса), не так ли?

Ну, id не ждет! После вызова uiRunnable.wait() фоновый поток немедленно продолжается, if (doCopy)... к if (doCopy)... Кажется, что фоновый поток и основной поток выполняются параллельно (неудивительно, так как это то, что делает поток …) и, следовательно, его состояние гонки: doCopy = false в потоке пользовательского интерфейса или if (doCopy) в фоновом потоке Достигается первым.

Как это возможно? Почему wait() работает, как описано? Или я что-то не так понял?

Большое спасибо!

EDIT: Чтобы избежать недоразумений: Конечно, я знаю методы жизненного цикла AsyncTask, но, насколько я понимаю, они не то, что я ищу (см. Мой ответ на комментарий).

Прерывая AsyncTask, как только потребуется взаимодействие с пользовательским интерфейсом, запросите пользовательский интерфейс и, конечно же, можно будет запустить новую AsyncTask. Однако это приведет к созданию кода, который очень трудно читать / понимать / поддерживать.

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

Основы

При первом onPreExecute() метод onPreExecute() запускается в потоке пользовательского интерфейса. Вы можете переопределить этот метод, чтобы внести изменения в пользовательский интерфейс перед doInBackground() метода doInBackground() .

После завершения doInBackground() метод onPostExecute() запускается в потоке пользовательского интерфейса, поэтому вы можете использовать его для внесения изменений в пользовательский интерфейс. Если вам необходимо внести регулярные изменения в doInBackground() пользовательского интерфейса во время doInBackground() вы переопределите метод onProgressUpdate() который выполняется в onProgressUpdate() пользовательского интерфейса, а затем вызовите его из doInBackground() , что позволит вам периодически обновлять интерфейс ,

Вы можете использовать что-то вроде следующего;

 private class DoStuffTask extends AsyncTask { @Override protected void doInBackground(Object... args) { // Do stuff onProgressUpdate(x); // Do more stuff } @Override protected void onProgressUpdate(Object... args) { // Update your UI here } } 

Теперь, если это не совсем так, и вы хотите, чтобы AsyncTask дождался ввода во время doInBackground() , вероятно, стоит рассмотреть возможность использования нескольких AsyncTasks . Затем вы можете закончить каждую AsyncTask , запросить ввод и затем запустить новую AsyncTask чтобы продолжить работу.

Учитывая, что экземпляры AlertDialog асинхронны, это, вероятно, предпочтительное решение, потому что вы можете запустить следующую AsyncTask из самого AlertDialog .

Использование wait () в AsyncTask

Если вы предпочитаете использовать один AsyncTask вы можете использовать wait изнутри вашей AsyncTask чтобы предотвратить выполнение, продолжающееся до тех пор, пока не будет выполнено какое-либо условие. Вместо использования нового Runnable мы просто используем два потока в этом экземпляре, поток, выполняющий doInBackground() и основной поток, и мы синхронизируем сам AsycTask .

Пример ниже;

 public class TestTask extends AsyncTask{ private boolean notified; private Promptable p; public interface Promptable { public abstract void prompt(); } public TestTask(Promptable p){ this.p = p; } @Override protected Object doInBackground(Object... arg0) { Log.d("First", "First"); onProgressUpdate(null); synchronized(this){ while(!notified){ try{ this.wait(); } catch(InterruptedException e){ } } } Log.d("Second", "Second"); return null; } @Override protected void onProgressUpdate(Object... args){ synchronized(this){ notified = true; p.prompt(); this.notify(); } } } в public class TestTask extends AsyncTask{ private boolean notified; private Promptable p; public interface Promptable { public abstract void prompt(); } public TestTask(Promptable p){ this.p = p; } @Override protected Object doInBackground(Object... arg0) { Log.d("First", "First"); onProgressUpdate(null); synchronized(this){ while(!notified){ try{ this.wait(); } catch(InterruptedException e){ } } } Log.d("Second", "Second"); return null; } @Override protected void onProgressUpdate(Object... args){ synchronized(this){ notified = true; p.prompt(); this.notify(); } } } 

В приведенном выше примере предположим, что ваша Activity анализируется в конструкторе AsyncTask и что он реализует интерфейс, который мы создаем, называемый Promptable . Вы заметите, что даже при вызове wait() мы помещаем его в цикл while. Если мы этого не сделали и как-то notify() вызвали до wait() ваш поток будет блокироваться бесконечно. Кроме того, вы не можете зависеть от того, что ваш поток будет ждать вечно, поэтому цикл while гарантирует, что он не будет продолжаться до тех пор, пока не будет вызвано уведомление.

Надеюсь, это поможет.