对于性能优化这个知识点来说,实在是太广了,博主本人也一直非常关注这方面的学习,而对于性能优化来说它包括了非常非常非常多方面,比如: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. Recycleview实现复杂布局

    Recycleview实现复杂布局 首先 附上Demo链接和效果供大家参考 Demo 实现思路 代码思考 时间是一切财富中最宝贵的财富. -- 德奥弗拉斯多 <a 实现思路 开始看到设计稿子的时 ...

  2. Settings.settings

    项目的设置,有些设置是不能变的,有些设置是由用户决定的,Settings.settings都能搞定. 范围是应用程序级别的是只读属性,范围是用户级别的可读写 读取的方式是: Properties.Se ...

  3. Spring统一返回Json工具类,带分页信息

    前言: 项目做前后端分离时,我们会经常提供Json数据给前端,如果有一个统一的Json格式返回工具类,那么将大大提高开发效率和减低沟通成本. 此Json响应工具类,支持带分页信息,支持泛型,支持Htt ...

  4. [转载]致创业者:APP已死 服务永生

    前几日,有位创业者和我讲他在带领团队做一个将爱踢球的人集中在一起的App,我告诉他你的创业方向错了.原因在于你的目的是要为爱踢球的人提供服务,而你现在却在竭尽全力的做App,你应该做的是设计你为爱踢球 ...

  5. jQuery AJAX 简介

    AJAX 是与服务器交换数据的技术,它在不重载全部页面的情况下,实现了对部分网页的更新. jQuery AJAX 实例 使用 jQuery AJAX 修改文本内容 尝试一下 » 什么是 AJAX? A ...

  6. Java中Semaphore(信号量)的使用

    Semaphore的作用: 在java中,使用了synchronized关键字和Lock锁实现了资源的并发访问控制,在同一时间只允许唯一了线程进入临界区访问资源(读锁除外),这样子控制的主要目的是为了 ...

  7. Angular2的input和output(原先的properties和events)

    angular2学习笔记 本文地址:http://blog.csdn.net/sushengmiyan 本文作者:苏生米沿 文章来源:http://blog.ng-book.com/angular-2 ...

  8. Windows下Java如何调用本地获取mac地址

    import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import ...

  9. Android底层开发经验

    最近看到一个博客,他的博文虽然是转载的,但源作者肯定对底层的理解可谓是非常透彻,一副思维导图就可以将整个重要体系建立起来,非常适合大家学习.学习不单单只要有代码,生动有趣更重要.在此推荐一波: htt ...

  10. Oracle11g R2创建PASSWORD_VERIFY_FUNCTION对应密码复杂度验证函数步骤

    Oracle11g R2创建PASSWORD_VERIFY_FUNCTION对应密码复杂度验证函数步骤 运行测试环境:数据库服务器Oracle Linux 5.8 + Oracle 11g R2数据库 ...