记一次列表加载超一万条数据优化

Android中列表是每个应用都会有的UI效果,而与用户的交互无非就是用户上下滑动、左右滑动、点击item 等等,本文就从小编遇到一次加载大量数据而影响体验优化之旅。

项目的列表采用RecycleView + BaseMultiItemQuickAdapter 分组效果,数据量10000~20000以上

数据拉取、缓存

首先是数据的获取方式,分页?还是全部获取? 这得考虑到后端的查询效率,数据库可以采用建立索引,提高效率,前端可以采用分页获取,并且为了提高体验,当然是存储在本地数据库了(NoSql),这里就有一个问题分页获取分页 再存储,数据库存储会导致阻塞线程,

果然出现了:Message Blocked in Main Thread XXXX ms,这是anr日志,说明,执行了耗时任务,UI 无响应。。。。。。。。。。
所以就想到了
这里采用了线程控制存储(建议采用线程池的方式来实现,不然物极必反,过多创建线程,浪费了资源。。。)

public CustomerReposity(){
      mThreadPool = new ThreadPoolExecutor(3, 5,
              10, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(128));
  }
 mThreadPool.execute(new Runnable() {
                          @Override
                          public void run() {
                              Log.d("customer","thread save to db ::"+data.list.size());
                              saveCustomerToLocalDB(data.list, pageNumer == 1);
                          }
                      });

从后端拉取到解析-再到数据库的时间

这边其实考虑到序列化,序列化有两种 Serializable\Parcelable,Serializable 采用反射机制,会导致大量对象产生,所以,考虑到性能的话,采用Parcelable

  @Override
  public void writeToParcel(Parcel out, int i) {
      out.writeString(cid);
      pingYin = in.readString();

  }

  public static final Parcelable.Creator<Customer> CREATOR = new Parcelable.Creator<Customer>() {
      @Override
      public Customer createFromParcel(Parcel source) {
          return new Customer(source);
      }

      @Override
      public Customer[] newArray(int size) {
          return new Customer[size];
      }
  };

总不能全部加载完毕才显示给用户看吧?

刚开始采用了数据监听(LiveData的方式,项目架构本身采用MVVM的方式)


 mReposity.getCustomerDefaultTeam().observe(this, new Observer<List<Customer>>() {
          @Override
          public void onChanged(@Nullable List<Customer> customers) {
              mAdapter.setNewData(customers);
          }
      });

又会有问题:数据边加载边刷新??? 列表一直notifyDataChanged…这边采用preFetch思想,先给用户展示部分数据

RecycleView 优化

布局优化

也就是item 布局,减少层级,这样就避免多次绘制UI,如果 Item 高度是固定的话,可以使用 RecyclerView.setHasFixedSize(true); 来避免 requestLayout 浪费资源,如果不要求动画,可以通过 ((SimpleItemAnimator) rv.getItemAnimator()).setSupportsChangeAnimations(false); 把默认动画关闭来提神效率,通过 getExtraLayoutSpace 来增加 RecyclerView 预留的额外空间(显示范围之外,应该额外缓存的空间),如下所示

mViewDataBinding.rvCustomers.setLayoutManager(new LinearLayoutManager(this){
         @Override
         protected int getExtraLayoutSpace(RecyclerView.State state) {
             return 100;
         }
     });
     mViewDataBinding.rvCustomers.setAdapter(mAdapter);
     mViewDataBinding.rvCustomers.setHasFixedSize(true);
     mViewDataBinding.rvCustomers.setItemViewCacheSize(20);
     ((SimpleItemAnimator) mViewDataBinding.rvCustomers.getItemAnimator()).setSupportsChangeAnima  mAdapter.expandAll();
     mCustomerReposity = CustomerReposity.getmInstances();

可参考资料来源:https://blankj.com/2018/09/29/optimize-recycler-view/

List Remove效率

在页面点击展开分组、收缩分组的时候发现,展开的时候很顺利,收缩的时候即卡顿个几秒,,看下日志ANR。。。。。,查看  BaseQuickAdapter  源码
public int collapse(@IntRange(from = 0) int position, boolean animate, boolean notify) {
       position -= getHeaderLayoutCount();

       IExpandable expandable = getExpandableItem(position);
       if (expandable == null) {
           return 0;
       }
       int subItemCount = recursiveCollapse(position);
       expandable.setExpanded(false);
       int parentPos = position + getHeaderLayoutCount();
       if (notify) {
           if (animate) {
               notifyItemChanged(parentPos);
               notifyItemRangeRemoved(parentPos + 1, subItemCount);
           } else {
               notifyDataSetChanged();
           }
       }
       return subItemCount;
   }

因此分析 原因估计是出在recursiveCollapse(position)这个方法 ,继续往下看


    @SuppressWarnings("unchecked")
    private int recursiveCollapse(@IntRange(from = 0) int position) {
        T item = getItem(position);
        if (!isExpandable(item)) {
            return 0;
        }
        IExpandable expandable = (IExpandable) item;
        int subItemCount = 0;
        if (expandable.isExpanded()) {
            List<T> subItems = expandable.getSubItems();
            if (null == subItems) return 0;

            for (int i = subItems.size() - 1; i >= 0; i--) {
                T subItem = subItems.get(i);
                int pos = getItemPosition(subItem);
                if (pos < 0) {
                    continue;
                }
                if (subItem instanceof IExpandable) {
                    subItemCount += recursiveCollapse(pos);
                }
                mData.remove(pos);
                subItemCount++;
            }
        }
        return subItemCount;
    }

看了下代码好像没做什么,就是删除分组下的item,就想到了,我一个分组有几十W+的item,它这样remove,是不是时间花太久了??????》〉》〉
果然 remove 方法执行耗时了。。。。。。开始改造



    /**
     * 改造源码****###
     * @param position
     * @return
     */
    private int recursiveCollapse(@IntRange(from = 0) int position) {
        MultiItemEntity item = getItem(position);
        if (!isExpandable(item)) {
            return 0;
        }
        IExpandable expandable = (IExpandable) item;
        if (expandable.isExpanded()) {
            List<MultiItemEntity> subItems = expandable.getSubItems();
            mData = removeAllChildList(mData,subItems);
        }
        return expandable.getSubItems().size();
    }

    public int collapsew(@IntRange(from = 0) int position, boolean animate) {
        return collapses(position, animate, true);
    }


    /**
     * 替代List.removeAll()的方法提高效率
     * @param source
     * @param destination
     * @return
     */
    public List<MultiItemEntity> removeAllChildList(List<MultiItemEntity> source, List<MultiItemEntity> destination) {
        List<MultiItemEntity> afterRemove = new LinkedList<MultiItemEntity>();
        Set<MultiItemEntity> destinaToRemove = new HashSet<MultiItemEntity>(destination);
        for (MultiItemEntity t : source) {
            if (!destinaToRemove.contains(t)) {
                afterRemove.add(t);
            }
        }
        return afterRemove;
    }
```java

# 博客:http://guosen.github.io/


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值