简体   繁体   中英

Using the result of an AsyncTask to run a for loop (Async again) inside its onPostExecute

I have the below code which I use to retrieve PhoneNumber data from my database. Before presenting it to the user, I want to match each PhoneNumber object to its respective contact (which is stored in another table) to also present the user with the full name of the retrieved phone number.

The issue arises when I need to query the database a second time, as I have to do it asynchronously (otherwise the app crashes). I also need to do it in a loop, for each PhoneNumber object. How can I implement this?

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;
}

Why do you use AsyncTask? It's deprecated for usage, and usually, no one uses it right now. There is no reason to execute such code in an activity/fragment where your widgets are located. I suggest using one of the MV* patterns at first. The best one is MVP for example. You can create class Presenter with such an interface:

public class Presenter {

  private Activity activity;

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

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

  void onPhoneNumberClick(String phoneNumber) {

  }
}

You should attach your activity (fragment) to its' presenter in onStart() callback and detach in onStop(). When in your click listener happens event then you should ask presenter do work in
onPhoneNumberClick method. Then you should implement this logic. You need som asynchronous job. It can be achieved by running new thread, but then you have to switch your thread to main in order to set fetched data. Now, rxJava2 is a popular mechanism that let your achieve asynchronous work without launching threads. So using rxJava2 it can be so:

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);
          }
        });
  }

So here you see new entity interactor. It runs and does all that computation staff "get a list of SuggestedContactItem by one phone number". But that code above just shows how it's easy to switch thread for computation and observation in rxJava2.

So and finally interactor for your target:

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;
  }
}

So here you can test all behaviours independently. If you need a list of Phone Numbers and SuggestedContactItems simultaneously then you can return Pair in Interactor. It more SOLID code and a better approach. If you have any questions, please, DM me.

I don't think AsyncTask is a good instrument for this task, besides it is deprecated, try using RxJava or if you don't want to use external libraries then use java.util.concurrent/coroutines

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM