Лучший способ управлять ProgressDialog от AsyncTask - PullRequest
9 голосов
/ 28 ноября 2011

Я хотел бы использовать AsyncTask для управления бизнес-логикой в ​​моем приложении. Каков наилучший шаблон для использования метода onProgressUpdate(...) для AsyncTask, определенного в отдельных файлах (не в качестве внутреннего класса Activity) У меня есть две идеи:
1. Самый простой способ: создать ProgressDialog в Activity (используя onCreateDialog(...) метод) и передать конструктору ссылку на мой подкласс AsyncTask (переопределить onProgressUpdate(...) внутри моего подкласса AsyncTask). Недостатком этого решения является использование компонентов пользовательского интерфейса внутри кода бизнес-логики.

FooTask1.java:

public class FooTask1 extends AsyncTask<Void, Integer, Void> {
private ProgressDialog mProgressDialog;

public FooTask1(ProgressDialog progressDialog) {
    super();
    mProgressDialog = progressDialog;
}

@Override
protected Void doInBackground(Void... unused) {
    // time consuming operation
    for (int i=0; i<=100; i++) {
        this.publishProgress(i);
        try {
            Thread.sleep(100);
        } catch (Exception e) {}
    }
    return null;
}

@Override
protected void onProgressUpdate(Integer... progress) {
    mProgressDialog.setProgress(progress[0]);
}

@Override
protected void onPostExecute(Void result) {
    mProgressDialog.dismiss();
}
}

FooActivity1.java:

public class FooActivity1 extends Activity {

  private static final int DIALOG_PROGRESS_ID = 0;
  private ProgressDialog mProgressDialog;

  @Override
  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);

      showDialog(DIALOG_PROGRESS_ID);
      new FooTask(mProgressDialog).execute();
  }

  @Override
  protected Dialog onCreateDialog(int id) {
      switch(id) {
          case DIALOG_PROGRESS_ID:
             mProgressDialog = new ProgressDialog(this);
             mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
             mProgressDialog.setMessage("Loading...");
             mProgressDialog.setCancelable(false);
             return mProgressDialog;
          default:
             return null;
      }
  }
}

2. Более сложный способ: переопределить метод onProgressUpdate(...) для AsyncTask внутри Activity класса:

FooTask2.java:

public class FooTask2 extends AsyncTask<Void, Integer, Void> {
@Override
protected Void doInBackground(Void... unused) {
    // time consuming operation
    for (int i=0; i<=100; i++) {
        this.publishProgress(i);
        try {
            Thread.sleep(100);
        } catch (Exception e) {}
    }
    return null;
}
}

FooActivity2.java

public class FooActivity2 extends Activity {

private static final int DIALOG_PROGRESS_ID = 0;
private ProgressDialog mProgressDialog;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    showDialog(DIALOG_PROGRESS_ID);
    new FooTaskLoader().execute();
}

@Override
protected Dialog onCreateDialog(int id) {
    switch(id) {
        case DIALOG_PROGRESS_ID:
            mProgressDialog = new ProgressDialog(this);
            mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            mProgressDialog.setMessage("Loading...");
            mProgressDialog.setCancelable(false);
            return mProgressDialog;
        default:
            return null;
    }
}

private class FooTaskLoader extends FooTask2 {
    @Override
    protected void onProgressUpdate(Integer... progress) {
        mProgressDialog.setProgress(progress[0]);
    }

    @Override
    protected void onPostExecute(Void result) {
        dismissDialog(DIALOG_PROGRESS_ID);
    }
}
}

Ответы [ 2 ]

13 голосов
/ 30 ноября 2011

Я бы лучше изолировал бизнес-логику от AsyncTask, чем изолировал AsyncTask от Activity.

В общем, AsyncTask имеет очень специфический дизайн и вариант использования в жизненном цикле приложения Android, то есть выполнить некоторое трудоемкое задание в фоновом потоке, после чего обновить представление Activity в потоке пользовательского интерфейса. Вот почему всегда рекомендуется использовать его как внутренний класс Activity.

Более функциональный ОО-дизайн IMO изолирует и централизует вашу бизнес-логику в POJO (для повторного использования). Для тестируемости вы можете сделать что-то вроде этого:
1. Определить интерфейс IBusinessDAO
2. Определить, что RealBusinessDAO реализует IBusinessDAO
3. Определить, MockBusinessDAO реализует IBusinessDAO
4. Позвоните IBusinessDAO.foo (); внутри AsyncTask.doInBackground ()

Для модульного тестирования вашей бизнес-логики, поскольку это POJO, вы можете использовать только JUnit для написания своего тестового примера. Иногда мы хотим протестировать компонент пользовательского интерфейса, и нам неважно, как реализована основная бизнес-логика, например, моя бизнес-логика подключается к удаленному http-серверу, загружает некоторые данные json, я не хочу делать это каждый раз, когда просто Я хочу протестировать макет пользовательского интерфейса, для этой ситуации я могу легко изменить свою активность, используя MockBusinessDAO (своего рода концепцию DI Spring), например:

public class MyActivity extends Activity {
  IBusinessDAO businessDAO;

  ... ...

  private class MyAsyncTask extends AsyncTask<Void, Void, Void> {
    ... ...        

    protected void doInBackground(Void... params) {
      businessDAO.foo();
    }
  }

  ... ...

  public void onCreate(Bundle savedInstanceState) {
    if (runInTest)
      businessDAO = new MockBusinessDAO();
    else
      businessDAO = new RealBusinessDAO();

    new myAsyncTask().execute();
  }


}

Вот некоторые преимущества:
1. Реализация AsyncTask проста и понятна (несколько строк кода в doInBacnground ())
2. Реализация бизнес-логики является чисто POJO, улучшите возможность повторного использования.
3. Изоляция тестовой бизнес-логики и компонента пользовательского интерфейса, улучшение тестируемости.

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

1 голос
/ 28 ноября 2011
  1. Вероятно, решение номер один - это то, как я бы с этим справился - это путь платформы Android. Поворот к этому решению (и, вероятно, как я справлюсь с этим, если AsyncTask не может вписаться в Activity -класс), я вместо этого передам Context в качестве параметра, а затем создам экземпляр и покажу ProgressDialog в onPreExecute.

  2. Решение № 2 в основном аналогично созданию диалога как внутреннего класса - так что вы можете также сделать это, если вы пойдете на это.

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