Проблемы с Android с многопоточной обработкой и диалогами - PullRequest
1 голос
/ 26 ноября 2011

Кажется, у меня постоянные проблемы с синхронизацией потоков и диалоговых окон. Я пытался использовать поток, или onCreate / onPrepare, или AsyncTask для некоторой загрузки / обработки в фоновом режиме. Чаще всего, когда фоновая обработка завершает и закрывает диалоговое окно, кажется, что элемент управления возвращается в корневой поток (поток Activity / UI?) до , когда диалог исчезает или выполняется процесс, подобный onPostExecute. , Это заставляет меня думать, что я делаю это неправильно. Вот типичная структура (псевдокод):

public class X {
  protected String result = null;
  protected ProgressDialog progressDialog;

  public void onCreate() {
    ...
    new XTask().execute();
    progressDialog.show();
    // result is null here, should be "hi"?

    // do things with result, like barf on a NPE...sigh
  }

  private class XTask extends AsyncTask {
    protected doInBackground() {
      // Get URL.
      // Look at contents, takes a few seconds.
      // Return the result (should get sent to onPostExecute).
    }
    protected onPostExecute(r) {
      result = r;
      progressDialog.dismiss();
    }
  }
}

Я думаю, что после того, как doPostExecute устанавливает результат и закрывает диалоговое окно, обработка продолжается в методе onCreate. Однако результат часто (не всегда) равен нулю на этом этапе в onCreate. Есть лучший способ сделать это? Это только из-за общей хреновости эмулятора?

Спасибо.

Edit: чтобы быть более конкретным, моя задача (в doInBackground) выбирает URL и выполняет небольшую обработку ответа. Этот процесс занимает 1-3 секунды. Затем теоретически onPostExecute устанавливает X.result в то, что было извлечено. Я знаю, что содержание URL является действительным, и ответ хороший (не нулевой). Проблема в том, что в течение этих 1-3 секунд элемент управления возвращается в onCreate, как будто progressDialog вообще никогда не жил (он вообще не отображается в эмуляторе, но я думаю, это нормально).

Я думал, что вызов dialog.show () является методом блокировки, т. Е. Диалоговое окно появилось, и этот метод не продолжится, пока не исчезнет, ​​но, похоже, это не так. Либо мой progressDialog.dismiss () вызывается до , следует, до установки X.result, либо вообще не вызывается, либо dismiss () происходит быстрее / до назначения, либо что-то еще полностью идет не так ...

Изменение порядка выполнения и progressDialog не помогает, равно как и перемещение его в onPreExecute (). Странно, onPostExecute не вызывается, пока я не вернусь в onCreate. Если у меня есть цикл Thread.sleep после выполнения (я думал, что это даст время), он никогда не завершит задачу, пока этот цикл не завершится, и я не вернусь. e.g.:

new XTask().execute();
int z=0;
while (response == null && z < 50) {
  Thread.sleep(500);
  z++;
}
if (response == null) return;

Метод задачи onPostExecute вызывается только после возврата. Хммм ... может быть, на onCreate это влияет.

Ответы [ 4 ]

1 голос
/ 27 ноября 2011

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

Если вы хотите выполнить какую-либо операцию с результатом, вы должны сделать это либо в onPostExecute XTask, либо после получения результата в doInBackground.

private class XTask extends AsyncTask {

     @Override
     protected void onProgressUpdate(/*params*/){
          //modify UI
     }
     protected doInBackground() {
     // Get URL.
     // Look at contents, takes a few seconds.

     //Option A: Now have the result, do some other processing here
     //Cant modify UI components from here, If you need to modify a UI component from           
     //here call publishProgress() and modify the component in onProgressUpdate()

     // Return the result (should get sent to onPostExecute).
}
protected onPostExecute(r) {

     result = r;
     //Option B do some processing on the result
     //You can modify UI components from here
     progressDialog.dismiss();
}

}

0 голосов
/ 27 ноября 2011

Помещение кода в onPostExecute задачи должно работать, простой тест предполагает, что это будет (для меня). Однако в итоге я написал другое решение, которое также работает. Я поместил большую часть кода в обработчик действия, который полностью отделяет его от потока пользовательского интерфейса. Мой onCreate просто показывает загрузочное окно ProgressDialog и все - он просто сидит, «загружается». Фоновая задача делает свое дело и по завершении отправляет сообщение обработчику. Это сообщение говорит обработчику закрыть диалоговое окно загрузки и заполнить список. При наличии ошибок отправляются разные сообщения, и обработчик отображает диалоговое окно с ошибкой.

0 голосов
/ 27 ноября 2011

Я думаю, что после того, как doPostExecute установит результат и закроет диалоговое окно, обработка затем продолжится в методе onCreate.

Это не так, когда вы вызываете new XTask ().execute () в потоке пользовательского интерфейса, приложение создает рабочий поток и начинает выполнять все, что вы определили в AsyncTask.doInBackground () в этом рабочем потоке, в этот момент (после вызова нового XTask (). execute ()) поток пользовательского интерфейса продолжает код выполненияпосле нового XTask (). execute ().

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

Если вы хотите заблокировать ваш поток пользовательского интерфейса и дождаться завершения AsyncTask после XTask (). Execute (), вы можете сделать следующее:

XTask xTask = new XTask();
xTask.execute();
progressDialog.show();
xTask.get() // <-- this will make your UI thread blocked and wait for AsycnTask at this point
// result is null here, should be "hi"?

Это возможно, но не хорошоНа практике, AsyncTask.get () будет блокировать выполнение в вызывающем потоке, поэтому, вероятно, получит исключение ANR.

Подводя итог
1. AysncTask.onPostExecute () - это место, где процесс возвращается из рабочего потока в поток пользовательского интерфейса, нам не важно, где и когда он будет вызываться в потоке пользовательского интерфейса, нам просто нужно убедиться, чтоон будет вызываться в потоке пользовательского интерфейса должным образом в какой-то момент в будущем.
2. AsyncTask.get (), вызывая этот метод, фактически заставляет AsyncTask работать синхронно с вызывающим потоком.

0 голосов
/ 26 ноября 2011

Я бы показал диалог прогресса до запуска AsyncTask. Обычно, когда выполняется AsyncTask, требуется некоторое время, чтобы завершить себя, и за это время остальная часть метода вызова задачи уже запущена. Но в вашем случае задача возвращается мгновенно, поэтому, вероятно, диалоговое окно появляется ПОСЛЕ того, как postexecute вызывается под AsyncTask.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...