Designing
for Performance
译者署名:qiongju@gmail.com
译者链接:http://admires.iteye.com/
版本:Android
3.1 r1
原文
http://developer.android.com/guide/practices/design/performance.html
性能优化
Android应用程序运行的移动设备受限于其运算能力,存储空间,及电池续航。由此,它必须是高效的。电池续航可能是一个促使你优化程序的原因,即使他看起来已经运行的足够快了。由于续航对用户的重要性,当电量耗损陡增时,意味这用户迟早会发现是由于你的程序。
虽然这份文档主要包含着细微的优化,但这些绝不能成为你软件成败的关键。选择合适的算法和数据结构永远是你最先应该考虑的事情,但这超出这份文档之外。
简介
写出高效的代码有两条基本的原则:
l不作没有必要的工作。
l尽量避免内存分配。
明智的优化
这份文档是关于Android规范的细微优化,所以先确保你已经了解哪些代码需要优化,并且知道如何去衡量你所做修改所带来的效果(好或坏)。开发投入的时间是有限的,所以明智的时间规划很重要。
(更多分析和笔记参见总结。)
这份文档同时确保你在算法和数据结构上作出最佳选择的同时,考虑API选择所带来的潜在影响。使用合适的数据结构和算法比这里的任何建议都更有价值,优先考虑API版本带来的影响有助于你找到更好的实现。(这在类库代码中更为重要,相比应用代码)
(如果你需要这样的建议,参见Josh
Bloch'sEffective
Java, item 47.)
在优化Android程序时,会遇到的一个棘手问题是,保证你的程序能在不同的硬件平台上运行。虚拟机版本和处理器各部相同,因此运行在之上的速度也大不一样。但这并且不是简单的A比B快或慢,并能在设备间做出排列。特别的,模拟器上只能评测出一小部分设备上体现的东西。有无JIT的设备间也存在着巨大差异,在JIT设备上好的代码有时候会在无JIT的设备上表现的并不好。
如果你想知道一个程序在设备上的具体表现,就必须在上面进行测试。
避免创建不必要的对象
对象创建永远不会是免费的。每个线程的分代GC给零时对象分配一个地址池以降低分配开销,但往往内存分配比不分配需要的代价大。
如果在用户界面周期内分配对象,就会强制一个周期性的垃圾回收,给用户体验增加小小的停顿间隙。Gingerbread中提到的并发回收也许有用,但不必要的工作应当被避免的。
因此,应该避免不必要的对象创建。下面是几个例子:
l如果有一个返回String的方法,并且他的返回值常常附加在一个StringBuffer上,改变声明和实现,让函数直接在其后面附加,而非创建一个短暂存在的零时变量。
l当从输入的数据集合中读取数据时,考虑返回原始数据的子串,而非新建一个拷贝.这样你虽然创建一个新的对象,但是他们共享该数据的char数组。(结果是即使仅仅使用原始输入的一部分,你也需要保证它的整体一直存在于内存中。)
一个更彻底的方案是将多维数组切割成平行一维数组:
lInt类型的数组常有余Integer类型的。推而广之,两个平行的int数组要比一个(int,int)型的对象数组高效。这对于其他任何基本数据类型的组合都通用。
l如果需要实现一个容器来存放元组(Foo,Bar),两个平行数组Foo[],Bar[]会优于一个(Foo,Bar)对象的数组。(例外情况是:当你设计API给其他代码调用时,应用好的API设计来换取小的速度提升。但在自己的内部代码中,尽量尝试高效的实现。)
通常来讲,尽量避免创建短时零时对象.少的对象创建意味着低频的垃圾回收。而这对于用户体验产生直接的影响。
性能之谜
前一个版本的文档给出了好多误导人的主张,这里做一些澄清:
在没有JIT的设备上,调用方法所传递的对象采用具体的类型而非接口类型会更高效(比如,传递HashMapmap比Map
map调用一个方法的开销小,尽管两个map都是HashMap).但这并不是两倍慢的情形,事实上,他们只相差6%,而有JIT时这两种调用的效率不相上下。
在没有JIT的设备上,缓存后的字段访问比直接访问快大概20%。而在有JIT的情况下,字段访问的代价等同于局部访问,因此这里不值得优化,除非你觉得他会让你的代码更易读(对于final
,static,及static
final变量同样适用)
用静态代替虚拟
如果不需要访问某对象的字段,将方法设置为静态,调用会加速15%到20%。这也是一种好的做法,因为你可以从方法声明中看出调用该方法不需要更新此对象的状态。
避免内部的Getters/Setters
在源生语言像C++中,通常做法是用Getters(i=getCount())代替直接字段访问(i=mCount)。这是C++中一个好的习惯,因为编译器会内联这些访问,并且如果需要约束或者调试这些域的访问,你可以在任何时间添加代码。
而在Android中,这不是一个好的做法。虚方法调用的代价比直接字段访问高昂许多。通常根据面向对象语言的实践,在公共接口中使用Getters和Setters是有道理的,但在一个字段经常被访问的类中宜采用直接访问。
无JIT时,直接字段访问大约比调用getter访问快3倍。有JIT时(直接访问字段开销等同于局部变量访问),要快7倍。在Froyo版本中确实如此,但以后版本可能会在JIT中改进Getter方法的内联。
对常量使用Static
Final修饰符
考虑下面类首的声明:

编译器会生成一个类初始化方法<clinit>,当该类初次被使用时执行,这个方法将42存入intVal中,并得到类文件字符串常量strVal的一个引用。当这些值在后面被引用时,他们通过字段查找进行访问。
我们改进实现,采用final关键字:

类不再需要<clinit>方法,因为常量通过静态字段初始化器进入dex文件中。引用intVal的代码,将直接调用整形值42;而访问strVal,也会采用相对开销较小的“字符串常量”(原文:“sring
constant”)指令替代字段查找。(这种优化仅仅是针对基本数据类型和String类型常量的,而非任意的引用类型。但尽可能的将常量声明为static
final是一种好的做法。
使用改进的For循环语法
改进for循环(有时被称为“for-each”循环)能够用于实现了iterable接口的集合类及数组中。在集合类中,迭代器让接口调用hasNext()和next()方法。在ArrayList中,手写的计数循环迭代要快3倍(无论有没有JIT),但其他集合类中,改进的for循环语法和迭代器具有相同的效率。
这里有一些迭代数组的实现:

zero()是当中最慢的,因为对于这个遍历中的历次迭代,JIT并不能优化获取数组长度的开销。
One()稍快,将所有东西都放进局部变量中,避免了查找。但仅只有声明数组长度对性能改善有益。
Two()是在无JIT的设备上运行最快的,对于有JIT的设备则和one()不分上下。他采用了JDK1.5中的改进for循环语法。
结论:优先采用改进for循环,但在性能要求苛刻的ArrayList迭代中,考虑采用手写计数循环。
(参见Effective
Javaitem 46.)
在私有内部内中,考虑用包访问权限替代私有访问权限
考虑下面的定义:

需要注意的关键是:我们定义的一个私有内部类(Foo$Inner),直接访问外部类中的一个私有方法和私有变量。这是合法的,代码也会打印出预期的“Value
is 27”。
但问题是,虚拟机认为从Foo$Inner中直接访问Foo的私有成员是非法的,因为他们是两个不同的类,尽管Java语言允许内部类访问外部类的私有成员,但是通过编译器生成几个综合方法来桥接这些间隙的。

内部类会在外部类中任何需要访问mValue字段或调用doStuff方法的地方调用这些静态方法。这意味着这些代码将直接存取成员变量表现为通过存取器方法访问。之前提到过存取器访问如何比直接访问慢,这例子说明,某些语言约会定导致不可见的性能问题。
如果你在高性能的Hotspot中使用这些代码,可以通过声明被内部类访问的字段和成员为包访问权限,而非私有。但这也意味着这些字段会被其他处于同一个包中的类访问,因此在公共API中不宜采用。
合理利用浮点数
通常的经验是,在Android设备中,浮点数会比整型慢两倍,在缺少FPU和JIT的G1上对比有FPU和JIT的Nexus
One中确实如此(两种设备间算术运算的绝对速度差大约是10倍)
从速度方面说,在现代硬件上,float和double之间没有任何不同。更广泛的讲,double大2倍。在台式机上,由于不存在空间问题,double的优先级高于float。
但即使是整型,有的芯片拥有硬件乘法,却缺少除法。这种情况下,整型除法和求模运算是通过软件实现的,就像当你设计Hash表,或是做大量的算术那样。
了解并使用类库
选择Library中的代码而非自己重写,除了通常的那些原因外,考虑到系统空闲时会用汇编代码调用来替代library方法,这可能比JIT中生成的等价的最好的Java代码还要好。典型的例子就是String.indexOf,Dalvik用内部内联来替代。同样的,System.arraycopy方法在有JIT的Nexus
One上,自行编码的循环快9倍。
(参见Effective
Javaitem 47.)
合理利用本地方法
本地方法并不是一定比Java高效。最起码,Java和native之间过渡的关联是有消耗的,而JIT并不能对此进行优化。当你分配本地资源时(本地堆上的内存,文件说明符等),往往很难实时的回收这些资源。同时你也需要在各种结构中编译你的代码(而非依赖JIT)。甚至可能需要针对相同的架构来编译出不同的版本:针对ARM处理器的GI编译的本地代码,并不能充分利用Nexus
One上的ARM,而针对Nexus
One上ARM编译的本地代码不能在G1的ARM上运行。
当你想部署程序到存在本地代码库的Android平台上时,本地代码才显得尤为有用,而并非为了Java应用程序的提速。
(参见Effective
Javaitem 54.)
结语
最后:通常考虑的是:先确定存在问题,再进行优化。并且你知道当前系统的性能,否则无法衡量你进行尝试所得到的提升。
这份文档中的每个主张都有标准基准测试作为支持。你可以在code.google.com“dalvik”项目中找到基准测试的代码。
这个标准基准测试是建立在Caliper
Java标准微基准测试框架之上的。标准微基准测试很难找到正确的路,所以Caliper帮你完成了其中的困难部分工作。并且当你会察觉到某些情况的测试结果并想象中的那样(虚拟机总是在优化你的代码的)。我们强烈推荐你用Caliper来运行你自己的标准微基准测试。
同时你也会发现Traceview对分析很有用,但必须了解,他目前是不不支持JIT的,这可能导致那些在JIT上可以胜出的代码运行超时。特别重要的,根据Taceview的数据作出更改后,请确保代码在没有Traceview时,确实跑的快了。
分享到:
相关推荐
### Android App性能优化的核心策略与实践 在移动设备上运行的Android应用程序,受限于设备的计算能力、存储空间以及电池续航能力,优化其性能成为开发者不可忽视的重要任务。本篇文章将深入探讨Android应用性能...
【Android APP性能优化总结】 Android应用的性能优化是开发者必须关注的重要环节,它关系到用户体验、应用评分以及用户留存率。在移动设备上,尤其是在竞争激烈的App市场中,快速响应和流畅的操作是留住用户的关键...
"Android APP性能优化分析" Android APP性能优化分析是指在Android应用程序中提高运行速度、流畅性和用户体验的技术手段和方法。从Android系统手机的特点出发,探讨Android APP性能优化的四个方面:流畅、稳定、...
android_app性能优化之list篇.ppt
尽管GT3.1.0不再更新,但它在性能测试方面的全面性仍然使其在Android app性能优化中占有重要地位。对于那些预算有限或者对perfdog收费模式有所顾忌的开发者来说,GT无疑是一个理想的选择。不过,使用旧版本也需要...
android界面及代码优化方案,帮助提升android应用的性能
在Android开发领域,性能优化是提升用户体验和应用质量的关键环节。这个"免费:Android性能优化视频百度云下载链接.rar"的压缩包文件提供了一个学习资源,可能是由一系列视频教程组成,帮助开发者深入理解和掌握...
通过Android Studio自带的Profiler工具,可以详细查看应用在启动时各个阶段的性能表现,进而定位问题所在。 6. 优化应用的清单文件(AndroidManifest.xml)。合理的配置清单文件可以减少系统对应用启动时间的影响,...
【Android性能优化典范 - 第6季 - 胡凯】主要涵盖了Android应用性能优化的关键方面,特别是关于程序启动时间和安装包大小的优化。以下是详细的知识点解析: 1. **程序启动时间优化**: - **启动时间的重要性**:...
Android Studio基于IntelliJ IDEA,提供了强大的代码编辑、调试、构建和性能优化工具。它支持Gradle构建系统,允许灵活的项目配置和依赖管理。通过XML布局文件和Java/Kotlin代码的结合,开发者可以构建复杂的用户...
Android性能优化技术在某体重管理APP中的应用研究 Android性能优化技术是 Android 应用开发中一个非常重要的方面。随着 Android 操作系统的普及, Android 应用程序的数量也在不断增加。为了在众多应用程序中...
《Android应用性能优化》是由人民邮电出版社出版的一本深入探讨Android应用性能提升的专业书籍。在移动设备领域,尤其是在竞争激烈的Android市场,提供流畅、高效的应用体验是开发者必须面对的关键挑战。这本书旨在...
《Android 360°全方面性能调优》是一本深度探讨Android系统性能优化的宝典,涵盖了设计思想、代码优化、程序性能、内存管理、功耗控制、网络通信、应用打包、屏幕适配、启动速度、流畅度、ANR问题、崩溃监控、OOM...
在Android应用程序开发中,程序书写和性能优化是至关重要的环节。本文主要探讨了一些基本的编程规范和性能分析工具的使用。 首先,遵循良好的编程规范可以提高代码的可读性和可维护性。在Android开发中,阿里巴巴...
总结,Android性能优化是一个综合性的任务,涉及到UI、启动、崩溃、卡顿、安全、网络等多个方面。开发者需要结合系统提供的优化方案、第三方库以及各种工具,进行全方位的优化,以提供更优质、更稳定的应用体验。
本文将探讨uni-app的运行原理,以及如何从这些原理出发解决性能优化问题。 首先,uni-app的运行机制涉及到逻辑层和视图层的分离。逻辑层负责业务逻辑和数据管理,而视图层则处理页面的渲染。在非H5端,逻辑层与视...
在Android应用开发中,性能优化是一项至关重要的任务,它直接影响着用户的体验和应用程序的市场竞争力。通过对应用进行优化,可以减少资源消耗,提高运行效率,降低内存占用,增强电池续航,提升用户界面的流畅性,...
【Android移动App性能测试】 性能测试是评估应用在各种条件下的运行效率和稳定性的关键环节。在Android平台上,性能测试涵盖了多个方面,如CPU使用率、内存占用、FPS(帧率)、流量消耗以及响应时间等。以下是这些...