使用AsyncTask的结果在其onPostExecute中运行一个for循环(再次使用异步)。

huangapple go评论75阅读模式
英文:

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

问题

以下是翻译好的代码部分:

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

            // 每次查询更新时清空列表
            suggestedContactList.clear();

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

                // 将这部分改为后台线程!
                returnedFullName = worksideDatabase.contactDao().getFullNameByContactId(id);

                // TODO - 将每个 PhoneNumber 与其联系人的姓名匹配
                SuggestedContactItem item =
                        new SuggestedContactItem(
                                returnedFullName, stringPN, pn.getPhoneNumberType());
                suggestedContactList.add(item);
            }
            adapter.notifyDataSetChanged();
        }
    }.execute();
    return returnedNumbers;
}
英文:

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&lt;PhoneNumber&gt; fetchMatchingNumbers(final String phoneNumber) {
    new AsyncTask&lt;Void, Void, List&lt;PhoneNumber&gt;&gt;() {
        @Override
        protected List&lt;PhoneNumber&gt; doInBackground(Void... voids) {
            returnedNumbers = worksideDatabase.phoneNumberDao().getMatchingNumbers(phoneNumber);

            return null;
        }

        @Override
        protected void onPostExecute(List&lt;PhoneNumber&gt; 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&#39;s name &amp; surname
                SuggestedContactItem item =
                        new SuggestedContactItem(
                                returnedFullName, stringPN, pn.getPhoneNumberType());
                suggestedContactList.add(item);
            }
            adapter.notifyDataSetChanged();
        }
    }.execute();
    return returnedNumbers;
}

答案1

得分: 2

为什么要使用 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) {

  }
}

您应该在onStart()回调中将您的活动(或片段)附加到其Presenter上,并在onStop()中进行分离。
当点击监听器中发生事件时,您应该要求Presenter在onPhoneNumberClick方法中执行工作。
然后您应该实现这个逻辑。您需要一些异步的工作。可以通过运行新线程来实现,但随后您必须切换到主线程以设置获取的数据。现在,RxJava2是一种流行的机制,可以在不启动线程的情况下实现异步工作。因此,使用RxJava2,可以这样写:

void onPhoneNumberClick(String phoneNumber) {
    Single.fromCallable(() -> interactor.getSuggestedContactItems(phoneNumber))
        .subscribeOn(Schedulers.io()) // 发生调用的IO线程
        .observeOn(AndroidSchedulers.mainThread()) // 结果被观察的线程
        .subscribe(contactItems -> {
          if (activity != null) {
            activity.setContactList(contactItems);
          }
        });
}

因此,您可以看到这里出现了一个新的实体interactor。它运行并执行所有计算工作,“通过一个电话号码获取建议的联系人列表”。但是上面的代码只是展示了如何在RxJava2中轻松切换计算和观察的线程。

最后,是针对您目标的Interactor:

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

      // 将此处更改为后台线程!
      String returnedFullName = worksideDatabase.contactDao().getFullNameByContactId(id);

      // TODO - 将每个PhoneNumber与其联系人的名字和姓氏进行匹配

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

因此,您可以独立测试所有这些行为。如果您需要同时获得电话号码列表和建议的联系人列表,则可以在Interactor中返回一个Pair。这将是更具有SOLID原则的代码,也是更好的方法。
如果您有任何问题,请给我发送直接消息。

英文:

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(() -&gt; interactor.getSuggestedContactItems(phoneNumber))
        .subscribeOn(Schedulers.io()) // thread from poll IO where invokation happens
        .observeOn(AndroidSchedulers.mainThread()) // thread where result observed
        .subscribe( contactItems -&gt; {
          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&lt;SuggestedContactItem&gt; getSuggestedContactItems(String phoneNumber) {
    List&lt;PhoneNumber&gt; returnedNumbers = worksideDatabase.phoneNumberDao().getMatchingNumbers(phoneNumber);
    List&lt;SuggestedContactItem&gt; suggestedContactItems = new ArrayList&lt;&gt;();
    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&#39;s name &amp; 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.

答案2

得分: 0

我认为AsyncTask不是这个任务的好工具,而且它已经被弃用了,尝试使用RxJava,或者如果你不想使用外部库,可以使用java.util.concurrent/coroutines。

英文:

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

huangapple
  • 本文由 发表于 2020年5月3日 16:00:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/61571309.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定