You need to enable JavaScript to run this app. Android AsyncTask等待任务完成取数填充ListView的实现问题
优惠活动
大模型
产品
解决方案
定价
更多

Android AsyncTask等待任务完成取数填充ListView的实现问题

问题根因
  • 在AsyncContactSearch的doInBackground中使用了Retrofit的enqueue异步方法发起网络请求,该方法不会阻塞当前线程等待请求结果:任务刚把网络请求发出去,就会跳过回调逻辑,直接执行后续的排序、返回操作,此时的listContact是空列表。
  • 外层代码虽然调用了mLoadContactTask.execute().get()等待AsyncTask返回结果,但本质上等的是这个“秒返回空列表”的任务,根本没有等待真正的网络请求完成。
  • 等后续Retrofit的异步回调真正拿到数据往集合里塞的时候,Adapter已经拿着之前的空列表完成了初始化和绑定,后续往集合中加数据的操作不会触发Adapter刷新,最终ListView始终显示空。
  • 补充隐患:在主线程调用AsyncTask的.get()方法阻塞等待结果,本身会引发主线程ANR问题,只是当前场景下AsyncTask空返回才没触发卡顿。
修正实现方案

核心逻辑是把异步层级拉平,不要出现异步任务嵌套异步请求的结构,利用AsyncTask自带的主线程回调完成视图绑定:

  1. 替换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的返回值传出。

  2. 移除主线程阻塞等待的写法,改用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.03 19:06:10