Android如何高性能编程

    如何实现Android的高性能编程,就涉及到Android的内存优化,算法优化,Android的界面优化,Android指令级优化,以及Android应用内存占用分析,还有其他的知识。

作为开发人员,算法和数据结构永远是我们最先考虑的事情,同时还要记得两个基本原则:1.不要做不必要的事;2.不要分配不必要的内存。

1)内存优化

Android系统对每个软件所能使用的RAM空间进行了限制(例如nexus one对每个软件的内存限制是24M),同时Java语言本身比较消耗内存,dalvik虚拟机也要占用一定的内存空间,所以合理使用内存,彰显出一个程序员的素质和技能。

JIT,即时编译,又称动态转译,是一种通过在运行时将字节码翻译为机器码,从而改善字节码编译语言性能的技术。即时编译前期的两个运行时理论是字节码编译和动态编译。Android原来Dalvik虚拟机是作为一种翻译器实现,新版(Android2.2+)将换成JIT编译器实现。性能测试显示,在多项测试中新版本比旧版本提升了大约6倍。

避免创建不必要的对象

虽然gc为每个线程都建立了临时对象池,可以使创建对象的代价变得小一些,但是分配内存永远都比不分配内存的代价大。如果你在用户界面循环中分配对象内存,就会引发周期性的垃圾回收,用户就会觉得界面像打嗝一样一顿一顿的。所以,除非必要,应尽量避免创建对象的实例。下面的例子将帮助你理解这条原则:当你从用户输入的数据中截取一段字符串时,尽量使用substring函数取得原始数据的一个子串,而不是为子串另外建立一份拷贝。这样你就有一个新的string对象,他与原始数据共享一个char数组。如果你有一个函数返回一个string对象,而你确切的知道这个字符串会被附加到一个stringbuffer,那么,请改变这个函数的参数和实现方式,直接把结果附加到stringbuffer中,而不要再建立一个短命的临时对象。

一个更极端的例子是,把多维数组分成多个一维数组:

int数组比integer数组好,这也概括了一个基本事实,两个平行的int数组比(int,int)对象数组性能要好很多。同理,这适用于所有基本类型的组合。如果你想用一种容器存储(Foo,Bar)元组,尝试使用两个单独的Foo[]数组和Bar[]数组,一定比(Foo,Bar)数组效率更高。(也有例外的情况,就是当你建立一个API,让别人调用它的时候,这时候你就要注重对API接口的设计而牺牲一点速度,当然在API的内部,你仍要尽可能的提高代码的效率)

总体来说,就是避免创建短命的临时对象。减少对象的创建就能减少垃圾收集,进而减少对用户体验的影响。

静态方法代替虚拟方法

如果不需要访问某对象的字段,将方法设置为静态,调用会加速15%到20%,这也是一种好的做法,因为你可以从方法声明中看出调用该方法不需要更新此对象的状态。从Smali指令级别来看,调用实例方法的指令时invoke-virtual而调用静态方法的指令是invoke-static,两个指令的区别在于invoke-virtual需要多一个本地寄存器(用于存当前对象this)。后期会对smali指令集优化做详细的介绍

避免内部getters/setters

在原生语言像C++中,通常做法是用getters(i==getCount())代替直接字段访问(i=mCount)。这是C++中一个好的习惯,因为编译器会内联这些访问,并且如果需要约束或者调试这些域的访问,你可以在任何时间添加代码。而在Android中,这不是一个好的做法。虚方法调用的代价比直接字段访问高昂许多。通常根据面向对象语言的实践,在公共接口中使用getters和setters是有道理的,但在一个字段经常被访问的类中宜采用直接访问。无JIT时,直接字段访问大约比调用getter访问快3倍,有JIT时(直接访问字段开销等同于局部变量访问),要快7倍,从Smali指令级别上来看,调用虚函数的指令时invoke-virtual,而直接访问类变量的指令时get,从官网的介绍来看,就说明iget指令的执行效果要比invoke-virtual高很多。

在多次调用全局变量的函数里,将全局变量赋值给本地变量

另一个相似的原则是:永远不要在for的第二个条件中调用任何方法。

性能优化:1对常量使用static final修饰符

将一个方法或类声明为final不会带来性能的提升,但是会帮助编译器优化代码。举例说,如果编译器知道一个getter方法不会被重载,那么编译器会对其采用内联调用。

2使用改进的for循环语法

3避免使用浮点数

通常的经验是,在Android设备中,浮点数会比整型慢两倍,在缺少FPU和JIT的G1上对比有FPU和JIT的Nexus One中确实如此(两种设备间算术运算的绝对速度差大约是10倍)从速度方面说,在现代硬件上,float和double之间没有任何不同。更广泛的讲,double大2倍。在台式机上,由于不存在空间问题,double的优先级高于float。但即使是整型,有的芯片拥有硬件乘法,却缺少除法,这种情况下,整型除法和求模运算是通过软件实现的,就像当你设计hash表,或是做大量的算术那样。

4了解并使用类库

5合理利用本地方法

本地方法并不是一定比Java高效。最起码,Java和native之间过渡的关联是有消耗的,而JIT并不能对此进行优化。当你分配本地资源时(本地堆上的内存,文件说明符等),往往很难时的回收这些资源,同时你也需要在各种结构中编译你的代码(而非依赖JIT)。甚至可能需要针对相同的架构来编译出不同的版本:针对ARM处理器的GI编译的本地代码,并不能充分利用NExus One上的ARM,而针对Nexus One上ARM编译的本地代码不能在G1的ARM上运行,当你想部署程序到存在本地代码库的Android平台上时,本地代码才显得尤为有用,而并非为了Java应用程序的提速。

6复杂算法尽量用C完成

复杂算法尽量用C或者C++完成,然后用JNI调用,但是如果是算法比较简单,不必这么麻烦,毕竟JNI调用也会花一定的时间,请权衡。

7减少不必要的全局变量

尽量避免static成员变量引用资源消耗过多的实例,比如context,避免内存泄漏,Android提供了很健全的消息传递机制(intent)和任务模型(handler),可以通过传递或事件的方式,防止一些不必要的全局变量。

8不要过多指望GC

Java的gc使用的是一个有向图,判断一个对象是否有效看的是其他的对象能到达这个对象的顶点,有向图的相对于链表、二叉树来说开销是可想而知,所以不要过多指望gc,将不同的对象可以把它指向NULL,并注意代码质量。

9了解Java四种引用方式

JDK1.2版开始,把对象的引用分为4种级别,从而使程序能更加灵活地控制对象的生命周期。这4种级别由高到低依次是:强引用、软引用、弱引用和虚引用。

10使用实体类比接口好

11避免使用枚举

12将与内部类一同使用的变量声明在包范围内

13缓存

适量使用缓存,不要过量使用,因为内存有限,能保存路径地址的就不要存放图片数据,不经常使用的尽量不要缓存,不用时就清空。在一些比较耗时的算法,且执行会有若干次的地方,加入LruCache。

14关闭资源对象


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值