使用 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 循环(再次异步)的主要内容,如果未能解决你的问题,请参考以下文章
对服务使用 AsyncTask 类仍然得到“跳过 31 帧!应用程序可能在其主线程上做太多工作。”。为啥?