Android ListView中复杂数据流的高效渲染

更新:目前已经写了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中取出即可:

Paste_Image.png

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进行缓存:

Paste_Image.png

复杂信息流

此处以微博为例:

  • 转发带视频类型


    Paste_Image.png
  • 普通文字+卡片类型
  • 转发图文类型

此外还有原创图文类型,原创视频,原创卡片,系统通知,转发视频,转发图文,……,微博有多达二十种左右的item类型,每种类型中的View可能包括头部图片、文字描述、正文内容、正文图片、正文视频、分享操作栏等内容,这些都缓存到内存中,再加上二十多种类型,想想内存的感受……

优化

我们可以看到很多类型中都有相同可以复用的部分,如头部、分享操作栏等很多item中都是一样,是否可单独拿出来呢,我们进行简单的拆分:

Paste_Image.png

一个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,滑动流畅度也提高不少,在低端手机上的效果尤其明显,掉帧明显减少。非常建议有需要的同学尝试。

Other

贴个不定期更新的公众号,欢迎交流:

wutongke

推荐阅读更多精彩内容