Android性能优化之被忽视的优化点

简介:

对于性能优化这个知识点来说,实在是太广了,博主本人也一直非常关注这方面的学习,而对于性能优化来说它包括了非常非常非常多方面,比如:I/O的优化、网络操作的优化、内存的优化、数据结构的优化、代码层次的优化、UI渲染优化、CPU资源使用率的优化、异常处理的优化等等等等。。。

Android性能优化之被忽视的优化点

本篇文章就博主本人的理解来讲述一些在Android开发中可以优化的地方

ArrayList和Vector

ArrayList和Vector都是内部以数组实现的List,它们两唯一的区别就是对多线程的支持,ArrayList是线程不安全的,而Vector内部对大多数方法都做了同步,是线程安全的,既然是线程安全的,所以性能方面肯定不如ArrayList了(当然想法肯定是对的),不过这需要看哪方面了,ArrayList在add、get、remove等操作效率肯定是高于Vector的,而在内存方面,Vector却比ArrayList表现的更好,这归根都是ArrayList的扩容策略导致的,稍后分析。

实现RandomAccess接口的集合使用fori遍历

先谈谈List集合的遍历方式,有三种:foreach、iterator、fori。

而在开发中一般需要遍历时首选肯定是foreach了,因为它效率高,这个观点没错,不过需要分场合了。

下面是我用这三种方式测试遍历有100w条数据的ArrayList集合:

  
  
  1. long start = System.currentTimeMillis(); 
  2. for (int i = 0; i < size; i++) { 
  3. data.get(i); 
  4. long end = System.currentTimeMillis(); 
  5. Log.v("zxy","fori花费:"+(end-start)); 
  6.  
  7. start = System.currentTimeMillis(); 
  8. for (Integer integer : data) { 
  9.  
  10. end = System.currentTimeMillis(); 
  11. Log.v("zxy","foreach花费:"+(end-start)); 
  12.  
  13. Iterator<Integer> iterator = data.iterator(); 
  14. start = System.currentTimeMillis(); 
  15. while (iterator.hasNext()){ 
  16. iterator.next(); 
  17. end = System.currentTimeMillis(); 
  18. Log.v("zxy","iterator花费:"+(end-start)); 
  19.  
  20. 11-19 09:11:44.276 1418-1418/? V/zxy: fori花费:30 
  21. 11-19 09:11:44.380 1418-1418/? V/zxy: foreach花费:105 
  22. 11-19 09:11:44.476 1418-1418/? V/zxy: iterator花费:95  

而通常我们所说的效率高的foreach在遍历上却显得不如意,而fori效率表现的最好,这是因为ArrayList和Vector集合内部实现由数组实现,所以随机访问的速度是很快的,对于可以进行随机访问的List,JDK为它们实现了RandomAccess接口,表示支持快速随机访问。

而在遍历有1w条数据的LinkedList集合时:

  
  
  1. 11-19 09:33:23.984 1737-1737/? V/zxy: fori花费:351 
  2. 11-19 09:33:23.988 1737-1737/? V/zxy: foreach花费:2 
  3. 11-19 09:33:23.992 1737-1737/? V/zxy: iterator花费:4  

则foreach表现最佳,所以对数组、或者实现了RandomAccess接口的List,遍历用fori性能最佳,对LinkedList等以链表实现的集合遍历时使用foreach或者iterator性能最佳,因为foreach的实现就是通过iterator实现的。

我们可以这样判断该List遍历用哪种方式:

  
  
  1. if (list instanceof RandomAccess) 
  2. for (int i = 0; i < list.size(); i++) {} 
  3. else { 
  4. Iterator<?> iterator = list.iterator(); 
  5. while (iterator.hasNext()) { 
  6. iterator.next(); 
  7.  

预知容量的情况下构造ArrayList时尽量指定初始大小

ArrayList内部的扩容策略是当其所存储的元素数量超过它已有的大小时,它就会以1.5倍的容量进行扩容,也就是假如当前ArrayList的容量为10000,那么它在需要再存储一个元素时,即第10001个元素,由于容量不够而进行一次扩容,而ArrayList扩容后的容量则变为了15000,而多出了一个元素就多了5000个元素的空间,这太浪费内存资源了,而且扩容还会导致整个数组进行一次内存复制,而ArrayList集合默认大小为10,因此合理的设置ArrayList的容量可避免集合进行扩容。ArrayList内部扩容和数组复制代码为:

  
  
  1. Object[] newArray = new Object[s + 
  2. (s < (MIN_CAPACITY_INCREMENT / 2) ? 
  3. MIN_CAPACITY_INCREMENT : s >> 1)]; 
  4. System.arraycopy(a, 0, newArray, 0, s); 
  5. array = a = newArray;  

而Vector内部扩容策略为按需扩容,每次+1:

  
  
  1. if (capacityIncrement <= 0) { 
  2. if ((adding = elementData.length) == 0) { 
  3. adding = 1; 
  4. else { 
  5. adding = capacityIncrement; 
  6.  
  7. E[] newData = newElementArray(elementData.length + adding);  

同样,在众多Map集合中也有各自扩容策略,比如HashMap每次扩容时新容量等于原始的容量*2。在我们常用做字符串拼接的StringBuffer和StringBuilder内部,实际上也是有扩容策略,默认为扩容为原始的1.5倍。

所以,在这些需要扩容的api上,如果预先知道了数据的大小,则预先设置,这样不仅可以避免扩容导致的空间浪费,而且还可避免内部调用System.arraycopy()进行大量数据复制。

程序如果需要通过索引下标对List做随机访问,应优先考虑ArrayList和Vector,迫不得已尽量不要使用LinkedList

虽说ArrayList在内存上比不上Vector,不过它对数据操作的效率高,特别是在Android等移动设备上,采取牺牲一点空间换时间的方式还是可取的,而涉及到线程安全方面,则使用Vector。

如果一个方法不需要使用该对象的成员,那么把该方法设为static

静态调用该方法比对象调用该方法快15%~20%,因为这样可以从方法签名上就可以看出该方法调用不会影响该对象的状态

巧用final关键字

final关键字一般在定义常量和方法用的比较多,而大多数人对final的理解往往是在不可变性上,而final对性能优化也有很大的作用。

比如:static int AGE = 10;当10在后面被引用时,这时会有一个字段查找的过程,对于int类型也就是查找方法区中的整型常量池,而对于final的常量,则省去了这个过程,比如:static final int AGE = 10;在使用到AGE的地方将直接用10代替。

不过对于上面这种优化技巧,仅对基本类型和String类型有效,对于其它的引用类型则无效,但是我们在声明常量的时候加上 static final 依然是个好习惯

对与final关键字,还有一个强大的作用,就是对那些使用频繁、已经确定为终态的方法定义final,这样有什么好处呢?

说这个前先来说说java中方法的执行过程吧,当调用某个方法时,首先这个方法会入栈,执行完毕后,这个方法出栈,资源释放,而这个过程内部其实是内存地址的转移过程,当执行入栈的方法时,其实就是把程序的执行地址转移到该方法存放的内存地址中,而做此操作前,还有必须进行原先程序执行的内存地址保存过程,当方法执行完出栈后则继续按保存的地址继续执行程序,而这个过程,就是方法的调用过程。

所以,方法的调用过程实际上是需要空间和时间的,而对于同一个方法的频繁调用的优化实际上就是使用内联的办法。

又说到内联函数,内联函数实际上是在编译期做的优化,编译器会将标为为内联的函数在其调用的地方直接用整个函数体进行替换掉,这就省去了函数调用所耗去的时间资源了,而换来的却是目标代码量的增加,所以内联这种优化策略实际上是采取了以空间换时间的策略,对于移动端来说,巧用内联函数实则非常有益。

而要是一个函数成为内联函数,就是将它定义为final,这样在程序编译时,编译器会自动将final函数进行内联优化,那么在调用该函数时则直接展开该函数体进行使用。

总结,并不是内联函数越多越好,一方面它对我们程序的运行效率上确实有提升,而另一方面,对于过多的使用内联函数,则会弄巧成拙,有可能会把某个方法的方法体越搞越大,而且对于某些方法体比较大的方法,内联展开的时间有可能超过方法调用的时间,所以这不仅不会提供性能,反而是降低了本该有的性能。

综合来看,我们可以对那些使用频繁、已经确定为终态的方法、方法体不大的方法用final修饰,提供程序的性能。

优先考虑系统中提供的代码而不是自己写

系统内置了许多非常方便的api供我们使用,比如:System、Arrays、Collections、String等内置了许多方法api,这比我们自己手写方便多了,除了这个外,对于Android来说许多api都使用了底层C/C++实现,所以效率上也比我们自己写快,同样,对于系统api,DVM往往也会使用内联的方式提高效率

慎用异常

慎用异常并不是不用异常,而是指程序中用抛异常的方式来执行某些操作,比如有些人会以强抛异常方式来中断某些操作等。因为抛异常时都会执行fillInStackTrace();方法,该方法作用就是重新调整堆栈,这使得没有必要用异常的地方一定要避免使用







本文作者:佚名
来源:51CTO
相关文章
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
538 4
Termux安卓终端美化与开发实战:从下载到插件优化,小白也能玩转Linux
Termux是一款安卓平台上的开源终端模拟器,支持apt包管理、SSH连接及Python/Node.js/C++开发环境搭建,被誉为“手机上的Linux系统”。其特点包括零ROOT权限、跨平台开发和强大扩展性。本文详细介绍其安装准备、基础与高级环境配置、必备插件推荐、常见问题解决方法以及延伸学习资源,帮助用户充分利用Termux进行开发与学习。适用于Android 7+设备,原创内容转载请注明来源。
84 19
掌握安卓性能优化的秘诀:电池寿命与运行效率的提升
【10月更文挑战第6天】 本文深入探讨了安卓应用开发中的性能优化技巧,重点分析了影响电池寿命和运行效率的关键因素,并提供了针对性的优化策略。通过代码优化、资源管理、后台任务处理等方法,开发者可以显著提升应用的续航能力和流畅度。同时,结合具体案例,展示了如何在实际开发中应用这些技巧,确保应用在各种场景下都能保持高效运行。本文旨在为安卓开发者提供实用的性能优化指导,助力其打造更优质的应用体验。
150 2
深入探索Android系统架构与性能优化
本文旨在为读者提供一个全面的视角,以理解Android系统的架构及其关键组件。我们将探讨Android的发展历程、核心特性以及如何通过有效的策略来提升应用的性能和用户体验。本文不包含常规的技术细节,而是聚焦于系统架构层面的深入分析,以及针对开发者的实际优化建议。
141 21
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
81 5
安卓开发中的性能优化技巧
【10月更文挑战第29天】在移动应用的海洋中,性能是船只能否破浪前行的关键。本文将深入探讨安卓开发中的性能优化策略,从代码层面到系统层面,揭示如何让应用运行得更快、更流畅。我们将以实际案例和最佳实践为灯塔,引领开发者避开性能瓶颈的暗礁。
107 3
Android经典面试题之图片Bitmap怎么做优化
本文介绍了图片相关的内存优化方法,包括分辨率适配、图片压缩与缓存。文中详细讲解了如何根据不同分辨率放置图片资源,避免图片拉伸变形;并通过示例代码展示了使用`BitmapFactory.Options`进行图片压缩的具体步骤。此外,还介绍了Glide等第三方库如何利用LRU算法实现高效图片缓存。
106 20
Android经典面试题之图片Bitmap怎么做优化

热门文章

最新文章