Использование результата AsyncTask для запуска для l oop (снова Asyn c) внутри его onPostExecute - PullRequest
1 голос
/ 03 мая 2020

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

Проблема возникает, когда мне нужно запросить базу данных во второй раз, поскольку я должен делать это асинхронно (в противном случае приложение вылетает). Мне также нужно сделать это в al oop, для каждого объекта PhoneNumber. Как я могу это реализовать?

public List<PhoneNumber> fetchMatchingNumbers(final String phoneNumber) {
    new AsyncTask<Void, Void, List<PhoneNumber>>() {
        @Override
        protected List<PhoneNumber> doInBackground(Void... voids) {
            returnedNumbers = worksideDatabase.phoneNumberDao().getMatchingNumbers(phoneNumber);

            return null;
        }

        @Override
        protected void onPostExecute(List<PhoneNumber> phoneNumbers) {
            super.onPostExecute(phoneNumbers);

            // Clear the list each time the query is updated
            suggestedContactList.clear();

            for (PhoneNumber pn : returnedNumbers) {
                int id = pn.getContactId();
                String stringPN = pn.getPhoneNumber();

                // Change this to be a background thread!
                returnedFullName = worksideDatabase.contactDao().getFullNameByContactId(id);

                // TODO - match each PhoneNumber to its contact's name & surname
                SuggestedContactItem item =
                        new SuggestedContactItem(
                                returnedFullName, stringPN, pn.getPhoneNumberType());
                suggestedContactList.add(item);
            }
            adapter.notifyDataSetChanged();
        }
    }.execute();
    return returnedNumbers;
}

Ответы [ 2 ]

2 голосов
/ 03 мая 2020

Почему вы используете AsyncTask? Это устарело для использования, и обычно никто не использует это прямо сейчас. Нет причин выполнять такой код в деятельности / фрагменте, где расположены ваши виджеты. Сначала я предлагаю использовать один из паттернов MV *. Лучший из них, например, MVP. Вы можете создать класс Presenter с таким интерфейсом:

public class Presenter {

  private Activity activity;

  void attachActivity(Activity activity) {
    this.activity = activity;
  }

  void detachActivity() {
    this.activity = null;
  }

  void onPhoneNumberClick(String phoneNumber) {

  }
}

Вы должны прикрепить свою активность (фрагмент) к его 'Presenter в обратном вызове onStart () и отключить в onStop (). Когда в вашем прослушивателе щелчка происходит событие, вы должны попросить докладчика выполнить работу в методе
onPhoneNumberClick. Затем вы должны реализовать эту логику c. Вам нужна некоторая асинхронная работа. Этого можно достичь, запустив новый поток, но затем вам нужно переключить ваш поток на основной, чтобы установить извлеченные данные. Теперь rxJava2 - это популярный механизм, который позволяет вам выполнять асинхронную работу без запуска потоков. Таким образом, используя rxJava2, это может быть так:

void onPhoneNumberClick(String phoneNumber) {
    Single.fromCallable(() -> interactor.getSuggestedContactItems(phoneNumber))
        .subscribeOn(Schedulers.io()) // thread from poll IO where invokation happens
        .observeOn(AndroidSchedulers.mainThread()) // thread where result observed
        .subscribe( contactItems -> {
          if (activity != null) {
            activity.setContactList(contactItems);
          }
        });
  }

Итак, здесь вы видите нового интерактивного субъекта. Он запускает и выполняет весь этот вычислительный персонал "получает список Recommended по одному номеру телефона". Но приведенный выше код просто показывает, как легко переключать потоки для вычислений и наблюдений в rxJava2.

Итак, наконец, интерактив для вашей цели:

class Interactor {
  private WorksideDatabase worksideDatabase;

  Interactor(WorksideDatabase worksideDatabase) {
    this.worksideDatabase = worksideDatabase;
  }

  List<SuggestedContactItem> getSuggestedContactItems(String phoneNumber) {
    List<PhoneNumber> returnedNumbers = worksideDatabase.phoneNumberDao().getMatchingNumbers(phoneNumber);
    List<SuggestedContactItem> suggestedContactItems = new ArrayList<>();
    for (PhoneNumber pn : returnedNumbers) {
      int id = pn.getContactId();
      String stringPN = pn.getPhoneNumber();

      // Change this to be a background thread!
      String returnedFullName = worksideDatabase.contactDao().getFullNameByContactId(id);

      // TODO - match each PhoneNumber to its contact's name & surname

      SuggestedContactItem item =
          new SuggestedContactItem(
              returnedFullName, stringPN, pn.getPhoneNumberType());
      suggestedContactItems.add(item);
    }
    return suggestedContactItems;
  }
}

Так что здесь вы можете независимо протестировать все варианты поведения. , Если вам нужен список телефонных номеров и RecommendedContactItems одновременно, вы можете вернуть Pair в Interactor. Это больше SOLID кода и лучший подход. Если у вас есть какие-либо вопросы, пожалуйста, DM me.

0 голосов
/ 03 мая 2020

Я не думаю, что AsyncTask - хороший инструмент для этой задачи, кроме того, он устарел, попробуйте использовать Rx Java или, если вы не хотите использовать внешние библиотеки, используйте java .util.concurrent / coroutines.

...