onPostExecute() вызывается, но не закрывает мой диалог

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

Проблема. Странное поведение заключается в том, что диалоговое окно прогресса появляется, но никогда не закрывается.

  • Я уверен, что onPostExecute() вызывается для каждого экземпляра задачи, так как любые операторы Log.d("","") срабатывают, если они размещены здесь, даже сообщения Toast появляются из этого метода, но я не могу закрыть статический диалог.

  • Я понимаю, что AsyncTask (ы) имеют доступ к потоку пользовательского интерфейса только в двух местах [onPreExecute() и onPostExecute()], поэтому я думаю, что пытаться закрыть диалоговое окно в runOnUiThread() не нужно.

  • Все вызовы executeTask() выполняются из разных методов onCreate() различных действий, которым необходимо получить некоторые данные по сети, прежде чем заполнять некоторые из их элементов пользовательского интерфейса, и я всегда передаю контекст текущего действия задачам.

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

  • Может ли это быть проблемой времени? Я также заметил, что в большинстве случаев DDMS показывает, что все задачи завершены до отображения активности. Если я использую new Handler().postDelayed(runnable_which_calls_these_tasks,10); в onCreate() и добавляю код задержки в foo_X(), действия отображаются без каких-либо задержек, но диалоговое окно просто не будет закрываться().

Я прочитал довольно много статей по этому вопросу, но до сих пор не могу понять, где именно я ошибаюсь. Я не хочу определять каждую задачу как private inner class Task1 extends AsyncTask<> во всех моих классах действий, и я бы не хотел (если только это не единственное решение) загружать объект приложения со всеми ссылками на действия, как упоминалось в этом обсуждении: Действительно ли AsyncTask концептуально ошибочен или я просто что-то упустил?.

Я потратил на это неделю и совершенно ничего не понимаю :( Было бы здорово, если бы кто-нибудь мог направить меня и сообщить, что мне не хватает.
Ниже приводится определение класса: [Я удалил некоторые нерелевантные приложения код для ясности]

public class NetworkTask extends AsyncTask<Void, Integer, Boolean> {  

private  Context UIcontext;  
private int operationType;  
private static ProgressDialog dialog;
private static int taskCount;


private NetworkTask(int operationType Context context){  
    this.UIcontext = context; 
    this.operationType = operationType;
    if (taskCount++ == 0)  
        dialog = ProgressDialog.show(context,"","Loading...");

}  

public static Boolean executeTask(int operationType, Context context) {
    return new NetworkTask(operationType, context).execute().get(); 
}

@Override  
protected void onPreExecute(){  
    super.onPreExecute();  
    if (taskCount == 1)  
        dialog.show();  
}  

@Override
protected Boolean doInBackground(Void... arg0) {  
switch(operationType){  
    case TYPE_1:  
        foo1();  
        break;  
    case TYPE_2:  
        foo2();  
        break;  
    case TYPE_3:  
        foo3();  
        break;  
    case TYPE_4:  
        foo4();  
        break;  
}  

@Override
protected void onPostExecute(Boolean result) {
    super.onPostExecute(result);
    taskCount--;
    if (dialog.isShowing() && taskCount == 0){
            dialog.dismiss();
    }else {
        Toast.makeText(UIcontext, "Task#"+ operationType+", m done, but there are "+taskCount+" more", 5).show();
    }
}

}


person ritwaj    schedule 15.11.2010    source источник


Ответы (1)


Отредактировано:

Тайна раскрыта - это доставило мне немного хлопот. Было несколько проблем:

  1. Основная проблема, из-за которой не отображалось диалоговое окно, заключалась в NetworkTask.get(). Это блокирующий вызов, который ожидает завершения всего NetworkTask. В течение этого времени он блокирует поток пользовательского интерфейса, поэтому ничего не рисуется (также другие элементы пользовательского интерфейса не отвечают). Удалить get():

    public static Boolean executeTask(int operationType, Context context){
        new NetworkTask(operationType, context).execute();
        return true; // return whatever
    } 
    
  2. show() в ProgressDialog вызывается дважды. Это показывает два диалога, один за другим. Удалите show() внутри onPreExecute().

  3. ProgressDialog является модальным — он предотвращает изменение пользовательского интерфейса, пока это не будет сделано. Toast.makeText() вызываются перед dialog.dismiss(), но поскольку диалоговое окно блокирует отрисовку на экране, они помещаются в очередь и отображаются после закрытия диалогового окна.

  4. super.onPostExecute(result) и super.onPreExecute() избыточны, поэтому их можно удалить.

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

person Peter Knego    schedule 15.11.2010
comment
@Peter Как я уже упоминал ранее, я хочу централизованно управлять всеми своими сетевыми задачами с помощью этого класса. количество задач — это просто статический счетчик, связанный с классом [уменьшается при каждом возврате задачи и увеличивается при каждом появлении задачи]. Мое намерение состоит в том, чтобы диалоговое окно прогресса отображалось первой задачей и закрывалось последней. [только что перепроверил, taskCount в моем коде статичен, извините за опечатку здесь] - person ritwaj; 15.11.2010
comment
@Peter Изменил код на основе вашего наблюдения, убедившись, что диалоговое окно отображается и закрывается ровно один раз. Диалог вообще не появляется. - person ritwaj; 15.11.2010
comment
Обновите приведенный выше код, чтобы отразить внесенные вами изменения. - person Peter Knego; 15.11.2010
comment
@Falmarri в целях тестирования я изменил все fooX(), чтобы они выглядели так private void foo1() { try { Thread.sleep(15000); }catch (Exception e) { } // network tasks done here}, разве это не должно гарантировать, что выполнение задач займет слишком много времени, поэтому каждый onPostExecute() вызывается через долгое время? Элементы пользовательского интерфейса загружаются, происходит значительная задержка (из-за спящего потока), а затем появляются всплывающие уведомления, но я не вижу диалогового окна. - person ritwaj; 16.11.2010
comment
@Питер Круто! теперь работает как шарм :) Теперь я понимаю, почему загрузка активности занимала целую вечность, несмотря на то, что длительные операции были разделены как AsyncTask. С get() я эффективно выполнял все свои операции последовательно. Однако небольшой вопрос: можете ли вы указать случай, когда мы хотели бы использовать get()? Разве блокировка на неопределенный срок не противоречит цели AsyncTask? Для меня get(long timeout, TimeUnit unit) имеет гораздо больше смысла, так как мы, вероятно, можем позволить себе подождать некоторое время в контексте нашего приложения. Еще раз спасибо за терпение :) - person ritwaj; 16.11.2010