安卓优化——常见API优化

String/StringBuilder/StringBuffer优化建议

字符串操作在Android应用开发中是十分常见的操作,也就是这个最简单的字符串操作却也暗藏很多潜在的性能问题,下面我们实例来说说。

先看下面这个关于String和StringBuffer的对比例子:

//性能差的实现 
String str1 = "Name:"; 
String str2 = "GJRS"; 
String Str = str1 + str2; 
//性能好的实现 
String str1 = "Name:"; 
String str2 = "GJRS"; 
StringBuffer str = new StringBuilder().append(str1).append(str2);

通过这个例子可以看出来,String对象(记得是对象,不是常量)和StringBuffer对象的主要性能区别在于String对象是不可变的,所以每次对String对象做改变操作(譬如“+”操作)时其实都生成了新的String对象实例,所以会导致内存消耗性能问题;而StringBuffer对象做改变操作每次都会对自己进行操作,所以不需要消耗额外的内存空间。

我们再看一个关于String和StringBuffer的对比例子:

//性能差的实现 
StringBuffer str = new StringBuilder().append("Name:").append("GJRS");
//性能好的实现
String Str = "Name:" + "GJRS";

在这种情况下你会发现StringBuffer的性能反而没有String的好,原因是在JVM解释时认为 String Str = "Name:" + "GJRS";就是String Str = "Name:GJRS";,所以自然比StringBuffer快了。

可以发现,如果我们拼接的是字符串常量则String效率比StringBuffer高,如果拼接的是字符串对象,则StringBuffer比String效率高,我们在开发中要酌情选择。当然,除过注意StringBuffer和String的效率问题,我们还应该注意另一个问题,那就是StringBuffer和StringBuilder的区别,其实StringBuffer和StringBuilder都继承自同一个父类,只是StringBuffer是线程安全的,也就是说在不考虑多线程情况下StringBuilder的性能又比StringBuffer高。

 

应用OnTrimMemory()实现性能建议

OnTrimMemory()是Android 4.0之后加入的一个回调方法,作用是通知应用在不同的情况下进行自身的内存释放,以避免被系统直接杀掉,提高应用程序的用户体验(冷启动速度是热启动的2~3倍)。系统会根据当前不同等级的内存使用情况调用这个方法,并且传入当前内存等级,这个等级有很多种,我们可以依据情况实现不同的等级,这里不详细介绍,但是要说的是我们应用应该至少实现如下等级:

  • TRIM_MEMORY_BACKGROUND 

内存已经很低了,系统准备开始根据LRU缓存来清理进程。这时候如果我们手动释放一些不重要的缓存资源,则当用户返回我们应用时会感觉到很顺畅,而不是重新启动应用。

可以实现OnTrimMemory方法的系统组件有Application、Activity、Fragement、 

Service、ContentProvider;关于OnTrimMemory释放哪些内存其实在架构阶段就要考虑清楚哪些对象是要常驻内存的,哪些是伴随组件周期存在的,一般需要释放的都是缓存。 

如下给出一个我们项目中常用的例子:

@Override public void onTrimMemory(int level) { 
    if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
     clearCache(); 
    } 
}

通常在我们代码实现了onTrimMemory后很难复显这种内存消耗场景,但是你又怕引入新Bug,想想办法测试。好在我们有一个快捷的方式来模拟触发该水平内存释放,如下命令:

adb shell dumpsys gfxinfo packagename -cmd trim value1

packagename为包名或者进程id,value为ComponentCallbacks2.java里面定义的值,可以为80、60、40、20、5等,我们模拟触发其中的等级即可。

应用HashMap与ArrayMap及SparseArray优化建议

在Android开发中涉及到数据逻辑部分大部分用的都是Java的API(譬如HashMap),但是对于Android设备来说有些Java的API并不适合,可能会导致系统性能下降,好在Google团队已经意识到这些问题,所以他们针对Android设备对Java的一些API进行了优化,优化最多就是使用了ArrayMap及SparseArray替代HashMap来获得性能提升。

HashMap:

HashMap内部使用一个默认容量为16的数组来存储数据,数组中每一个元素存放一个链表的头结点,其实整个HashMap内部结构就是一个哈希表的拉链结构。HashMap默认实现的扩容是以2倍增加,且获取一个节点采用了遍历法,所以相对来说无论从内存消耗还是节点查找上都是十分昂贵的。

SparseArray:

SparseArray比HashMap省内存是因为它避免了对Key进行自动装箱(int转Integer),它内部是用两个数组来进行数据存储的(一个存Key,一个存Value),它内部对数据采用了压缩方式来表示稀疏数组数据,从而节约内存空间,而且其查找节点的实现采用了二分法,很明显可以看见性能的提升。

ArrayMap:

ArrayMap内部使用两个数组进行数据存储,一个记录Key的Hash值,一个记录Value值,它和SparseArray类似,也会在查找时对Key采用二分法。

有了上面的基本了解我们可以得出结论供开发时参考,当数据量不大(千位级内)且Key为int类型时使用SparseArray替换HashMap效率高;当数据量不大(千位级内)且数据类型为Map类型时使用ArrayMap替换HashMap效率高;其他情况下HashMap效率相对高于二者。

应用ContentProviderOperation优化建议

ContentProvider是Android应用开发的核心组件之一,有时候在开发中需要使用ContentProvider对多行数据进行操作,我们的做法一般是多次调运相关操作方法,殊不知这种实现方式是非常低性能的,取而代之的做法应该是使用批量操作,具体为了使批量更新、插入、删除数据操作更加方便官方提供了ContentProviderOperation工具类。所以在我们开发中遇到类似情景时请务必使用批量操作,具体的优势如下:

  • 所有的操作都在一个事务中执行,可以保证数据的完整性。

  • 批量操作在一个事务中执行,所以只用打开、关闭一个事务。

  • 减轻应用程序与ContentProvider间的多次频繁交互,提升性能。

可以看见,这对于数据库操作来说是一个非常有用的优化措施,烦请务必重视(我们项目优化过,的确有很大提升)。

其他逻辑优化建议

关于API及逻辑性能优化其实有多知识点的,这里无法一一列出,只能给出一些重要的知识点,下面再给出一些常见的优化建议:

  • 避免在Android中使用Java的枚举类型,因为编译后不但占空间,加载也费时,完全没有static final的变量好用、高效。

  • Handler发送消息时尽量使用obtain去获取已经存在的Message对象进行复用,而不是新new Message对象,这样可以减轻内存压力。

  • 在使用后台Service时尽量将能够替换为IntentService的地方替换为此,这样可以减轻系统压力、省电、省内存、省CPU占用率。

  • 在当前类内部尽量不要通过自己的getXXX、setXXX对自己内部成员进行操作,而是直接使用,这样可以提高代码执行效率。

  • 不要一味的为了设计模式而过分的抽象代码,因为代码抽象系数与代码加载执行时间成正比。

  • 尽量减少锁个数、减小锁范围,避免造成性能问题。

  • 合理的选择使用for循环与增强型for循环,譬如不要在ArrayList上使用增强型for循环等。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值