更新:目前已经写了demo,欢迎讨论:Android复杂数据流的“高效”渲染
我们知道Android中的ListView之所以可以实现item的无限加载,是因为对每个item的View 进行了缓存复用。ListView的高效性能使得其在App开发中使用非常频繁,本文主要分析在复杂数据展示时如何更加高效的使用ListView,如微博、facebook、twitter等的feed流需要展示非常多的数据类型:新闻、图片、网页链接、视频,这种情况下ListView进行需要缓存各种类型的View,App的内存占用急剧升高……
ListView复用原理
1. 简单列表复用
首先简单介绍一下ListView的复用原理,我们知道使用ListView时一般需要结合Adapter使用,继承BaseAdapter时,一般需要实现四个方法:
@Override
public int getCount() {
return 0;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
}
其中getView是渲染每个Item时进行回调生成View的,方法参数convertView就是ListView传回可以复用的View,当其不为null时,无需重新创建View,可以直接使用convertView,进行数据渲染即可。其原理是当第一次调用时ListView直接将生成的View缓存到一个ArrayList<View>中,当需要时直接从ArrayList中取出即可:
2. 复杂列表复用
当列表中有多种类型的view时,我们需要实现BaseAdapter中的:
@Override
//返回view类型数量
public int getViewTypeCount() {
return super.getViewTypeCount();
}
@Override
//返回每个Item的类型
public int getItemViewType(int position) {
return super.getItemViewType(position);
}
这种情况下ListView实际为每种类型的Item设置了一个ArrayList进行缓存:
复杂信息流
此处以微博为例:
-
转发带视频类型
- 普通文字+卡片类型
- 转发图文类型
此外还有原创图文类型,原创视频,原创卡片,系统通知,转发视频,转发图文,……,微博有多达二十种左右的item类型,每种类型中的View可能包括头部图片、文字描述、正文内容、正文图片、正文视频、分享操作栏等内容,这些都缓存到内存中,再加上二十多种类型,想想内存的感受……
优化
我们可以看到很多类型中都有相同可以复用的部分,如头部、分享操作栏等很多item中都是一样,是否可单独拿出来呢,我们进行简单的拆分:
一个Item我们把它拆为来五个部分,首先头部、评论操作栏等可以在很多不同类型的数据Item中进行复用,文字、图片等的View也可以单独进行复用,而且最重要的是:缓存ArrayList中保存的View数量将会减少,内存消耗减了不少。
具体实现中的坑
看到这里,是不是很多同学觉得打开了新世界的大门,急着进行代码的优化?具体的代码不方便贴出来,这里说一下具体实现过程中碰到的坑:
-
item click事件
由于优化的需求,把逻辑上的一个Item拆分为了多个item,因此每个item上都要设置ItemClick事件。具体实现时可以写一个基类,在基类中对item click进行处理。 -
cover 按压效果
在item 点击时,一般需要有按压效果,此时逻辑上的item已经进行了拆分,需要策略实现逻辑上item的整体按压,而不是只有某个拆分后的item被按压。 -
divider
我们知道listview的item之间是有divider的,此时需要设置divider为null,我们通过添加item的方式来实现divider效果。
效果
等填完拆分后的坑,运行程序,观察前后的效果,内存占用可以减少10~20m,滑动流畅度也提高不少,在低端手机上的效果尤其明显,掉帧明显减少。非常建议有需要的同学尝试。