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

本篇文章就博主本人的理解来讲述一些在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集合:

        long start = System.currentTimeMillis();
        for (int i = 0; i < size; i++) {
            data.get(i);
        }
        long end = System.currentTimeMillis();
        Log.v("zxy","fori花费:"+(end-start));

        start = System.currentTimeMillis();
        for (Integer integer : data) {

        }
        end = System.currentTimeMillis();
        Log.v("zxy","foreach花费:"+(end-start));

        Iterator<Integer> iterator = data.iterator();
        start = System.currentTimeMillis();
        while (iterator.hasNext()){
            iterator.next();
        }
        end = System.currentTimeMillis();
        Log.v("zxy","iterator花费:"+(end-start));
11-19 09:11:44.276 1418-1418/? V/zxy: fori花费:30
11-19 09:11:44.380 1418-1418/? V/zxy: foreach花费:105
11-19 09:11:44.476 1418-1418/? V/zxy: iterator花费:95

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

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

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

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

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

        if (list instanceof RandomAccess)
        {
            for (int i = 0; i < list.size(); i++) {}
        } else {
            Iterator<?> iterator = list.iterator();
            while (iterator.hasNext()) {
                iterator.next();
            }
        }

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

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

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

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

        if (capacityIncrement <= 0) {
            if ((adding = elementData.length) == 0) {
                adding = 1;
            }
        } else {
            adding = capacityIncrement;
        }

        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();方法,该方法作用就是重新调整堆栈,这使得没有必要用异常的地方一定要避免使用


欢迎补充。。。

Android性能优化之被忽视的优化点的更多相关文章

  1. Android性能优化之被忽视的Memory Leaks

    起因 写博客就像讲故事.得有起因,经过,结果,人物.地点和时间.今天就容我给大家讲一个故事. 人物呢.肯定是我了. 故事则发生在近期的这两天,地点在coder君上班的公司.那天无意中我发现了一个奇怪的 ...

  2. android 性能优化

    本章介绍android高级开发中,对于性能方面的处理.主要包括电量,视图,内存三个性能方面的知识点. 1.视图性能 (1)Overdraw简介 Overdraw就是过度绘制,是指在一帧的时间内(16. ...

  3. Android性能优化典范第一季

    2015年伊始,Google发布了关于Android性能优化典范的专题,一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍了Android系统中有关 ...

  4. [转]Android性能优化典范

    2015年伊始,Google发布了关于Android性能优化典范的专题,一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍了Android系统中有关 ...

  5. [Android Pro] Android性能优化典范第一季

    reference to : http://www.cnblogs.com/hanyonglu/p/4244035.html#undefined 2015年伊始,Google发布了关于Android性 ...

  6. Android优化—— Google 发布 Android 性能优化典范

    阅读目录 0)Render Performance 1)Understanding Overdraw 2)Understanding VSYNC 3)Tool:Profile GPU Renderin ...

  7. Android性能优化典范(转)

    转载自oschina. 2015年伊始,Google发布了关于Android性能优化典范的专题, 一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍 ...

  8. Android性能优化之如何避免Overdraw

    什么是Overdraw? Overdraw就是过度绘制,是指在一帧的时间内(16.67ms)像素被绘制了多次,理论上一个像素每次只绘制一次是最优的,但是由于重叠的布局导致一些像素会被多次绘制,而每次绘 ...

  9. Google 发布 Android 性能优化典范

    2015年伊始,Google发布了关于Android性能优化典范的专题, 一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍了Android系统中有 ...

随机推荐

  1. ajax 304 bug处理方法

    在ie内核中,发现Ajax的请求不会真正的被发送到服务器端,返回的永远是304.这个应该是IE的设计问题,查询解决方法后,看到网上的一段话: "因为ajax请求的时候如果使用get方式请求, ...

  2. 用js实现排列组合

    在leetcode上看到一个题,代码实现排列组合的. 记得大学上课时候,就用c写过,现在用js试试,顺便看看耗时. 先看看3的阶乘: function permute(temArr,testArr){ ...

  3. Python中enumerate用法详解

    enumerate()是python的内置函数.适用于python2.x和python3.xenumerate在字典上是枚举.列举的意思enumerate参数为可遍历/可迭代的对象(如列表.字符串)e ...

  4. Java面试17|Java基础

    Linux上配置Java基础环境: https://www.cnblogs.com/kerrycode/archive/2015/08/27/4762921.html 1.final相关 (1)fin ...

  5. git 学习笔记(常用命令)

    1.新建一个文件,如果没有使用git add 命令将它提交到暂存区,那么这个文件就还没有被跟踪. 2.通过配置.gitignore文件可以指定要忽略的文件,被忽略的文件夹是不会被提交到暂存区的.所以这 ...

  6. FJUT寒假作业第二周C题解(位运算)

    题目来源:http://210.34.193.66:8080/vj/Contest.jsp?cid=161#P2 题意比较好理解.如果直接按题目要求一步一解.一定超时.作为一个懒人也不会这么暴力一个肯 ...

  7. 牛客网编程练习之PAT乙级(Basic Level):1034 写出这个数

    AC代码: import java.util.*; /** * @author CC11001100 */ public class Main { public static void main(St ...

  8. 这交互炸了(四) :一分钟让你拥有微信拖拽透明返回PhotoView

    本文已授权微信公众号:鸿洋(hongyangAndroid)原创首发 <交互炸了>或许是一系列高端特效教程, 文中会介绍一些比较炫酷的特效,以及实现的思路.特效实现本身也许不会有太大的难度 ...

  9. GDAL打开mdb文件失败解决方法

    使用GDAL打开mdb文件时提示下面错误信息: ERROR 1: Unable to initialize ODBC connection to DSN for DRIVER=Microsoft Ac ...

  10. SpringMVC+Spring+Mybatis整合,使用druid连接池,声明式事务,maven配置

    一直对springmvc和mybatis挺怀念的,最近想自己再搭建下框架,然后写点什么. 暂时没有整合缓存,druid也没有做ip地址的过滤.Spring的AOP简单配置了下,也还没具体弄,不知道能不 ...