Android AsyncTask等待任务完成取数填充ListView的实现问题
问题根因
- 在
AsyncContactSearch的doInBackground中使用了Retrofit的enqueue异步方法发起网络请求,该方法不会阻塞当前线程等待请求结果:任务刚把网络请求发出去,就会跳过回调逻辑,直接执行后续的排序、返回操作,此时的listContact是空列表。 - 外层代码虽然调用了
mLoadContactTask.execute().get()等待AsyncTask返回结果,但本质上等的是这个“秒返回空列表”的任务,根本没有等待真正的网络请求完成。 - 等后续Retrofit的异步回调真正拿到数据往集合里塞的时候,Adapter已经拿着之前的空列表完成了初始化和绑定,后续往集合中加数据的操作不会触发Adapter刷新,最终ListView始终显示空。
- 补充隐患:在主线程调用AsyncTask的
.get()方法阻塞等待结果,本身会引发主线程ANR问题,只是当前场景下AsyncTask空返回才没触发卡顿。
修正实现方案
核心逻辑是把异步层级拉平,不要出现异步任务嵌套异步请求的结构,利用AsyncTask自带的主线程回调完成视图绑定:
替换Retrofit的异步调用为同步执行
将doInBackground中的searchWithTerms.enqueue(...)逻辑替换为同步请求,让后台线程真的等到网络结果返回后再执行后续排序逻辑:// 替换原有enqueue的异步回调逻辑 try { Response<ContactList> response = searchWithTerms.execute(); if (!response.isSuccessful() || response.body() == null) { return listContact; } List<Contact> contacts = response.body().getRows(); for (Contact c : contacts) { listContact.add(ContactsManager.getInstance().addFromAPI(c)); } } catch (IOException e) { e.printStackTrace(); return listContact; }这一步保证
doInBackground会在网络请求完成、联系人列表填充并排序完成后,才将结果作为AsyncTask的返回值传出。移除主线程阻塞等待的写法,改用AsyncTask自带回调完成视图绑定
删掉原来fetchContactResponse中使用.get()阻塞拿结果的逻辑,将Adapter初始化、绑定的代码移到AsyncTask执行完成后的主线程回调中,保证数据完全加载完成后再操作视图:- 在
AsyncContactSearch中新增加载完成的回调接口,重写onPostExecute方法触发回调:public class AsyncContactSearch extends AsyncTask<Void, Void, List<Contact>> { // 新增加载回调接口 public interface OnContactLoadListener { void onLoadFinished(List<Contact> loadedContacts); } // 原有成员变量保留,新增回调引用 private OnContactLoadListener loadListener; // 构造方法补充接收回调参数 public AsyncContactSearch(Context context, int limit, int offset, String view, String search, OnContactLoadListener listener) { // 原有变量赋值逻辑不变 this.loadListener = listener; } @Override protected void onPostExecute(List<Contact> result) { super.onPostExecute(result); // 后台任务全流程完成后,在主线程触发回调 if (loadListener != null) { loadListener.onLoadFinished(result); } } // 原有doInBackground逻辑保留,注意去掉原来的enqueue、换成同步网络请求 } - 调整
changeContacts方法的逻辑,去掉阻塞调用,把Adapter初始化逻辑放到回调中:private void changeContacts() { if (mOnlyDisplayContacts) { // 启动异步加载任务,绑定加载完成的回调 AsyncContactSearch loadTask = new AsyncContactSearch( context, limit, offset, mView, search, loadedContacts -> { // 数据完全加载完成后,再初始化Adapter、绑定ListView listContact = loadedContacts; contactAdapter = new ContactsAdapter(context, listContact, this); mContactsList.setAdapter(contactAdapter); contactAdapter.notifyDataSetChanged(); } ); loadTask.execute(); } else { // 原有其他分支逻辑保持不变 } }
- 在
额外注意点:原有代码中排序用的Comparator缺失泛型,强转LinphoneContact的操作存在类型转换异常风险,建议给Comparator补上泛型<Contact>,避免运行时崩溃。
内容的提问来源于stack exchange,提问作者Riccardo Pezzolati
相关产品推荐
相关产品推荐

