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

Posted

技术标签:

【中文标题】使用 AsyncTask 的结果在其 onPostExecute 内运行 for 循环(再次异步)【英文标题】:Using the result of an AsyncTask to run a for loop (Async again) inside its onPostExecute 【发布时间】:2020-08-17 15:28:04 【问题描述】:

我有以下代码,用于从我的数据库中检索电话号码数据。在将其呈现给用户之前,我想将每个 PhoneNumber 对象与其各自的联系人(存储在另一个表中)进行匹配,以便还向用户显示检索到的电话号码的全名。

当我需要第二次查询数据库时会出现问题,因为我必须异步执行(否则应用程序崩溃)。对于每个 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;

【问题讨论】:

【参考方案1】:

为什么要使用 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() 回调中将您的活动(片段)附加到它的演示者,并在 onStop() 中分离。 当您的点击侦听器发生事件时,您应该要求演示者在其中工作 onPhoneNumberClick 方法。 然后你应该实现这个逻辑。你需要一些异步工作。它可以通过运行新线程来实现,但是您必须将线程切换到主线程才能设置获取的数据。现在,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);
          
        );
  

所以在这里你看到了新的实体交互器。它运行并执行所有计算人员“通过一个电话号码获取 SuggestedContactItem 列表”。但是上面的代码只是展示了在 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;
  

因此,您可以在这里独立测试所有行为。如果您同时需要电话号码和 SuggestedContactItems 列表,则可以在 Interactor 中返回 Pair。它有更多的 SOLID 代码和更好的方法。 如果您有任何问题,请私信我。

【讨论】:

嗨,奥列格。感谢您的详细回答。事实是,我没有使用或看过您建议的任何方法(不是 MVP,也不是 rxJava2,也不是交互器)。此外,我不知道 AsyncTask 在 Android 11 中已被弃用(尽管我认为这不是问题,因为他们无法完全删除它 - 这会破坏向后兼容性?)。我会调查您提出的所有建议,然后回复您提问或选择您的答案。 目前,我可能会使用 Java 的协程框架并实现该功能。并希望用您之后的建议替换所有内容。【参考方案2】:

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

【讨论】:

我可以在纯 Java 代码中同时使用 RxJava 和 Coroutines 吗?我以为 Coroutines 是 Kotlin 独有的? 你说得对,协程只在 Kotlin 中使用,java 使用 java.util.concurrent 中自己的并发机制

以上是关于使用 AsyncTask 的结果在其 onPostExecute 内运行 for 循环(再次异步)的主要内容,如果未能解决你的问题,请参考以下文章

如何在Android中强制停止AsyncTask [重复]

对服务使用 AsyncTask 类仍然得到“跳过 31 帧!应用程序可能在其主线程上做太多工作。”。为啥?

模型中 OnPost 中的更改绑定属性无效

将 AsyncTask 与 RecyclerView 一起使用:没有结果,但 asynctask 似乎正在工作

无法在 Razor 页面中调用 onPost

OnPost 后如何将模型保留在视图中?