android 性能优化

转自http://hukai.me/

高效容器

针对Android这种移动平台,SparseArrayArrayMap用来代替HashMap在有些情况下能带来更好的性能提升。

SparseArray

SparseArrayHashMap更省内存,在某些条件下性能更好,主要是因为它避免了对key的自动装箱(int转为Integer类型),它内部则是通过两个数组来进行数据存储的,一个存储key,另外一个存储value,为了优化性能,它内部对数据还采取了压缩的方式来表示稀疏数组的数据,从而节约内存空间,我们从源码中可以看到keyvalue分别是用数组表示:

privateint[] mKeys;
private Object[] mValues;

SparseArray只能存储keyint类型的数据,同时,SparseArray在存储和读取数据时候,使用的是二分查找法。

  • 添加数据

    public void put(int key, E value)

  • 删除数据

    public void remove(int key)

    public void delete(int key)

  • 获取数据

    public E get(int key, E valueIfKeyNotFound)

    public E get(int key)

  • 查询

    • 获取对应的key:
      publicintkeyAt(int index)

    • 获取对应的value:
      public E valueAt(int index)

虽说SparseArray性能比较好,但是由于其添加、查找、删除数据都需要先进行一次二分查找,所以在数据量大的情况下性能并不明显,将降低至少50%。

满足下面两个条件我们可以使用SparseArray代替HashMap:

  • 数据量不大,最好在千级以内
  • key必须为int类型,这中情况下的HashMap可以用SparseArray代替。
ArrayMap

ArrayMap是一个<key,value>映射的数据结构,它设计上更多的是考虑内存的优化,内部是使用两个数组进行数据存储,一个数组记录keyhash值,另外一个数组记录Value值,它和SparseArray一样,也会对key使用二分法进行从小到大排序,在添加、删除、查找数据的时候都是先使用二分查找法得到相应的index,然后通过index来进行添加、查找、删除等操作,所以,应用场景和SparseArray的一样,如果在数据量比较大的情况下,那么它的性能将退化至少50%。

  • 添加数据

    public V put(K key, V value)

  • 删除数据

    public V remove(Object key)

  • 获取数据

    public V get(Object key)

  • 查询

    • 获取对应的key:
      public K keyAt(int index)

    • 获取对应的value:
      public V valueAt(int index)

ArrayMap应用场景:

  • 数据量不大,最好在千级以内
  • 数据结构类型为Map类型

ArrayMap在循环遍历:

for(int i=0;i<map.size();i++){
    Object keyObj=map.keyAt(i);
    Object valObj=map.valueAt(i);    
    ...
}

为了避免autoboxing带来的效率问题,Android特地提供了一些如下的Map容器用来替代HashMap,不仅避免了autoboxing,还减少了内存占用:

容器类型
SparseBoolMap<bool,obj>
SparseIntMap<int,obj>
SparseLongMap<long,obj>
LongSparseMap<long,obj>


enum

使用enum之后的dex大小会变大,而且使用enum,运行时还会产生额外的内存占用

Android官方强烈建议不要在Android程序里面使用到enum。


Object Pools

在程序里面经常会遇到的一个问题是短时间内创建大量的对象,导致内存紧张,从而触发GC导致性能问题。对于这个问题,我们可以使用对象池技术来解决它。通常对象池中的对象可能是bitmaps,views,paints等等。

使用对象池技术有很多好处,它可以避免内存抖动,提升性能,但是在使用的时候有一些内容是需要特别注意的。通常情况下,初始化的对象池里面都是空白的,当使用某个对象的时候先去对象池查询是否存在,如果不存在则创建这个对象然后加入对象池,但是我们也可以在程序刚启动的时候就事先为对象池填充一些即将要使用到的数据,这样可以在需要使用到这些对象的时候提供更快的首次加载速度,这种行为就叫做预分配。使用对象池也有不好的一面,程序员需要手动管理这些对象的分配与释放,所以我们需要慎重地使用这项技术,避免发生对象的内存泄漏。为了确保所有的对象能够正确被释放,我们需要保证加入对象池的对象和其他外部对象没有互相引用的关系。


Index 还是Iterate

遍历容器是编程里面一个经常遇到的场景。在Java语言中,使用Iterate是一个比较常见的方法。可是在Android开发团队中,大家却尽量避免使用Iterator来执行遍历操作。下面我们看下在Android上可能用到的三种不同的遍历方法:

  • List(iterate)
for(Iterate it=list.iterate();it.hasNext();){
    Object obj=it.next();
    ...
}
  • List(For-Index)
int size=list.size();
for(int i=0;i<size;i++){
    Object obj=list.get(i);
    ...
}
  • List(Simplified)
for(Object obj:list){
    ...
}

使用上面三种方式在同一台手机上,使用相同的数据集做测试,他们的表现性能如下所示:

这里写图片描述

从上面可以看到for index的方式有更好的效率,但是因为不同平台编译器优化各有差异,我们最好还是针对实际的方法做一下简单的测量比较好,拿到数据之后,再选择效率最高的那个方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值