Android官方开发文档Training系列课程中文版:性能优化建议
原文地址:http://android.xsoftlab.net/training/articles/perf-tips.html
本篇文章主要介绍那些能够提升总体性能的微小优化点。它与那些能突然改观性能效果的优化手段并不属于同一类。选择正确的算法与数据结构必定是我们的第一总则。可是这不是我们这篇文章要介绍的。你应该将这篇文章所提及的知识点作为编码的日常习惯,这能够提升常规代码的执行效率。
下面是书写代码的基本准则:
- 绝不要做你不须要的工作。
- 假设能够不申请内存就不要申请。要合理复用已有的对象。
另一个较复杂的问题就是被优化过的APP肯定是要执行在各种类型的硬件平台上。不同版本号的虚拟机执行在不同的处理器上肯定会有不同的执行速度。须要特别说明的是。在模拟器上測试非常少会得知其他设备的性能。
在不同设备上另一个非常大的不同点就是有没有JIT(JIT的意思是即时编译器):在JIT设备上执行的最优代码并不总在没有JIT设备上有效。
为了确保APP能够在各类设备上执行良好,要确保代码在各个版本号的平台上都是高效的。
避免创建不必要的对象
创建对象绝不是没有成本的。尽管分代垃圾收集器能够使暂时对象的分配成本变得非常低,可是内存分配的成本总是远高于非内存分配的成本。
随着很多其他对象的生成,你可能就開始关注垃圾收集器了。尽管Android 2.3中出现的并发收集器可能会帮到你。可是不必要的工作总是应该避免的。
因此,要避免创建不须要的对象。
下面的演示样例可能会帮到你:
- 假设你有个返回字符串的方法。该方法所返回的字符串总是被接在一个StringBuffer对象后面。那么就能够更改此方法的实现方式:让该字符串直接跟在StringBuffer的后面返回。
这样就能够避免创建那些暂时性的变量。
- 当从字符串中提取子串时,应该尝试返回原始数据的子串,而不是创建一个副本。子串将会创建一个新的String对象,可是它与char[]共用的是同一数据。採用这样的方式的唯一不足就是:尽管使用了当中的一部分数据。可是剩余的数据还都保留在内存中。
一条更为先进的法则就是,将多维数组转换为平行数组使用:
- int数组的效率要比Integer数组的效率高的多。
- 假设你须要实现一个用于存储(Foo,Bar)对象的数组,要记得使用两个平行的Foo[],Bar[]数组,这要比单一的(Foo,Bar)数组效率好太多。
通常来说。要尽量避免创建那些生命周期非常短的暂时变量。
更少的对象创建意味着更低频率的垃圾回收,这会直接反应到用户体验上。
首选静态
假设不须要訪问对象的属性,那么就能够将方法设置为静态方法。这样调用将会添加15%-20%的速度。这还是一个好的习惯。由于这样能够告诉其他方法一个信号:它们更改不了对象的状态。
使用常量
请先考虑下面声明:
static int intVal = 42;
static String strVal = "Hello, world!";
编译器会产生出一个类的实例化方法,名为< clinit>。它会在类首次被用到的时候执行。
该方法会将值42存到intVal中,并将字符串常量表中的引用赋给strVal。
当这些值被引用之后,其他属性才干够訪问它们。
我们能够使用”final”关键字来改进一下:
static final int intVal = 42;
static final String strVal = "Hello, world!";
这样的话。类就不须要再调用< clinit>方法。由于常量的初始化工作被移入了dex文件里。代码能够直接引用intVal为42的值。而且訪问strVal也会直接得到字符串”string constant” ,这样能够省去了查找字符串的过程。
Note: 这样优化手段仅仅适用于基本数据类型以及字符串常量。不要作用其他类型。
避免内部的get\set方法
像C++这样的本地语言通常都会使用get方法来訪问属性。这对C++来说是一个非常好的习惯。而且C#、Java等面向对象语言也广泛使用这样的方式,由于编译器一般会进行内联訪问,而且假设你须要限制訪问或者调试属性的话,仅仅须要加入代码就能够。
只是,这在Android上并非个好习惯。方法调用的开销是非常大的。尽管为了遵循面向对象语言提供get、set方法是合理的,可是在Android中最好是能够直接訪问对象的字段。
在没有JIT的设备中。直接訪问对象字段的速度要比通过get方法訪问的速度快3倍。
在含有JIT的设备中,这个效率会达到7倍之多。
注意:假设你使用了ProGuard,那么就有了一个两全其美的结果,由于ProGuard会直接为你进行内联訪问。
使用增强for循环
增强for循环可用于实现了Iterable接口的集合或数组。
在集合内部,迭代器须要实现接口方法:hasNext()以及next()。
有下面几种訪问数组的方式:
static class Foo {
int mSplat;
}
Foo[] mArray = ...
public void zero() {
int sum = 0;
for (int i = 0; i < mArray.length; ++i) {
sum += mArray[i].mSplat;
}
}
public void one() {
int sum = 0;
Foo[] localArray = mArray;
int len = localArray.length;
for (int i = 0; i < len; ++i) {
sum += localArray[i].mSplat;
}
}
public void two() {
int sum = 0;
for (Foo a : mArray) {
sum += a.mSplat;
}
}
zero()方法是最慢的,由于JIT不能够对每次訪问数组长度的开销进行优化。
one()方法是稍快点的。它将一切元素放入了本地变量。这样避免了每一次的查询。
仅仅有数组的长度提供了明显的性能提升。
two()方法是最快的。它使用了增强for循环。
所以应当在默认情况下使用增强for循环。
Tip: 也能够查看Josh Bloch 的 Effective Java,第46条。
考虑使用包内訪问
请先思考下面类定义:
public class Foo {
private class Inner {
void stuff() {
Foo.this.doStuff(Foo.this.mValue);
}
}
private int mValue;
public void run() {
Inner in = new Inner();
mValue = 27;
in.stuff();
}
private void doStuff(int value) {
System.out.println("Value is " + value);
}
}
上面的代码定义了一个内部类。它能够直接訪问外部类的私有成员以及私有方法。这是正确的。这段代码将会打印出我们所期望的”Value is 27”。
这里的问题是:VM会觉得Foo$Inner直接訪问Foo对象的私有成员是非法的,由于Foo和Foo$Inner是两个不同的类。尽管Java语言同意内部类能够直接訪问外部类的私有成员(PS:虚拟机与语言是两种互不干扰的存在)。为了弥补这样的差异,编译器专门为此生成了一组方法:
/*package*/ static int Foo.access$100(Foo foo) {
return foo.mValue;
}
/*package*/ static void Foo.access$200(Foo foo, int value) {
foo.doStuff(value);
}
当内部类代码须要訪问属性mValue或者调用doStuff()方法时会调用上面这些静态方法。上面的代码归结为你所訪问的成员属性都是通过訪问器方法訪问的。早期我们说通过訪问器訪问要比直接訪问慢非常多,所以这是一段特定语言形成的隐性性能开销演示样例。
避免使用浮点型
一般来说,在Android设备上浮点型要比整型慢大概2倍的速度。
在速度方面,float与double并没有什么区别。在空间方面。double是float的两倍大。所以在桌面级设备上,假设空间不是问题,那么我们应当首选double。而不是float。
还有。在对待整型方面,某些处理器擅长乘法,不擅长除法。
在这样的情况下,整型的除法与取模运算都是在软件中进行的。假设你正在设计一个哈希表或者做其他大量的数学运算的话,这些东西应该考虑到。
使用本地方法要当心
使用本地代码开发的APP并不一定比Java语言编写的APP高效多少。首先。它会花费在Java-本地代码的转换过程中,而且JIT也不能优化到这些边界。
假设你正在申请本地资源,那么对于这些资源的收集能明显的感觉到困难。
除此之外,你还须要对每一种CPU架构进行单独编译。你可能甚至还须要为同一个CPU架构编译多个不同的版本号:为G1的ARM处理器编译的代码不能执行在Nexus One的ARM处理上,为Nexus One的ARM处理器编译的代码也相同不能执行在G1的ARM处理器上。
本地代码在这样的情况下适宜採用:当你有一个已经存在的本地代码库,你希望将它移植到Android上时,不要为了改善Java语言所编写的代码速度而去使用本地代码。
假设你须要使用本地代码。那么应该读一读JNI Tips.
Tip: 相关信息也能够查看Josh Bloch 的 Effective Java,第54条。
性能误区
在没有JIT的设备中,通过详细类型的变量调用方法要比抽象接口的调用要高效,这是事实。
举个样例。通过HashMap map调用方法要比Map map调用方法要高效的多,开销也少,尽管这两个实现都是HashMap。
其实速度并不会慢2倍这么多。真实的不同大概有6%的减缓。进一步讲。JIT会使两者的区别进一步缩小。
在没有JIT的设备上,通过缓存訪问属性要比重复訪问属性要快将近20%的速度。在JIT的设备中。属性訪问的花销与本地訪问的花销基本一致。所以这不是一项有多少价值的优化手段。除非你觉得这样做的话代码更易读(这对static,final,常量相同适用)。
常常估測
在開始优化之前,要确保你有个问题须要解决:要确保你能够精准測量现有的性能,否则将不能观察到优化所带来的提升。
基准点由Caliper的微型基准点框架创建。基准点非常难正确获得,所以Caliper将这份非常难处理的工作做了。甚至是在你没有在測量那些你想測量的地方的时候它也在工作。我们强烈的推荐你使用Caliper来创建自己的微型基准点。
你可能还发现Traceview非常有助于提升性能。只是你应该意识到Traceview工作的时候JIT并没有开启。这会错误的觉得JIT会将损失掉的时间弥补回来。这尤其重要:依据Traceview所更改的结果会使实际代码执行的更快。
有关很多其他提升APP性能的工具及方法。请參见下面文档:
Android官方开发文档Training系列课程中文版:性能优化建议的更多相关文章
- Android官方开发文档Training系列课程中文版:目录
Android官方开发文档Training系列课程中文版:目录 引言 在翻译了一篇安卓的官方文档之后,我觉得应该做一件事情,就是把安卓的整篇训练课程全部翻译成英文,供国内的开发者使用,尤其是入门开 ...
- Android官方开发文档下载
Android官方开发文档 docs-24_r02.rar(链接:https://pan.baidu.com/s/12xC998JeUHj3ndfDXPM2ww 密码:bxyk) ADT下载.Andr ...
- 在线API,桌面版,jquery,css,Android中文开发文档,JScript,SQL掌用实例
学习帮助文档大全 jquery,css,Android中文开发文档,JScript,SQL掌用实例 http://api.jq-school.com/
- Android基础开发文档汇总
一.Android 基本组件 1. Android中PackageManager使用示例 : http://blog.csdn.net/qinjuning/article/details/68678 ...
- [翻译]开发文档:android Bitmap的高效使用
内容概述 本文内容来自开发文档"Traning > Displaying Bitmaps Efficiently",包括大尺寸Bitmap的高效加载,图片的异步加载和数据缓存 ...
- 微信小程序 开发文档
官方开发文档: 小程序公众平台 小程序开发者指南 小程序开发者文档 学习资源: 微信:官方入门教程 微信:WeUI 是一套同微信原生视觉体验一致的基础样式库 微信:微信小程序示例 视频: 学堂在线:学 ...
- Android 界面滑动实现---Scroller类 从源码和开发文档中学习(让你的布局动起来)
在android学习中,动作交互是软件中重要的一部分,其中的Scroller就是提供了拖动效果的类,在网上,比如说一些Launcher实现滑屏都可以通过这个类去实现.. 例子相关博文:Androi ...
- Android 滑动界面实现---Scroller类别 从源代码和开发文档了解(让你的移动布局)
在android学习,行动互动是软件的重要组成部分,其中Scroller是提供了拖动效果的类,在网上.比方说一些Launcher实现滑屏都能够通过这个类去实现.. 样例相关博文:Android 仿 窗 ...
- Android App签名打包 与 SDK开发文档
Android App签名打包签名的意义1.为了保证每个程序开发者的合法权益2.放置部分人通过使用相同的Package Name来混淆替换已经安装的程序,从而出现一些恶意篡改3.保证我们每次发布的版本 ...
随机推荐
- myEclipse配置java版本(环境、项目、编译)
从别的地方导入一个项目的时候,经常会遇到eclipse/Myeclipse报Description Resource Path Location Type Java compiler level d ...
- 使用RabbitMQ实现延迟任务
场景一:物联网系统经常会遇到向终端下发命令,如果命令一段时间没有应答,就需要设置成超时. 场景二:订单下单之后30分钟后,如果用户没有付钱,则系统自动取消订单. 上述类似的需求是我们经常会遇见的问题. ...
- 指定一个M3U8文件,判断它包含的TS文件是不是都存在。指定一个Office生成的Swf文件,判断它包含的Swf文件是不是完整都存在。
static void Main(string[] args) { //检查M3u8文件 var fiPath = @"D:\Work\CloudPlatformUtil\CloudPlat ...
- java基础面试题-2
第一,谈谈final, finally, finalize的区别. final---修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承.因此一个类不能既被 ...
- C6000 CSL 函数说明
转自:http://bbs.21ic.com/icview-741800-1-1.html 先来看一个例子 代码1 CSL_FINST(osdRegs->VIDWINMD, OSD_VIDWIN ...
- POJ-1511 Invitation Cards (单源最短路+逆向)
<题目链接> 题目大意: 有向图,求从起点1到每个点的最短路然后再回到起点1的最短路之和. 解题分析: 在求每个点到1点的最短路径时,如果仅仅只是遍历每个点,对它们每一个都进行一次最短路算 ...
- HDU 1358 Period (kmp求循环节)(经典)
<题目链接> 题目大意: 意思是,从第1个字母到第2字母组成的字符串可由某一周期性的字串(“a”) 的两次组成,也就是aa有两个a组成: 第三行自然就是aabaab可有两个aab组成: 第 ...
- 洛谷 P1464 Function【记忆化搜索】
题目链接 题目描述 对于一个递归函数w(a,b,c) 如果a<=0 or b<=0 or c<=0就返回值1. 如果a>20 or b>20 or c>20就返回w ...
- Jmeter的安装和启动错误总结,出现unable to access jarfile apachejmeter.jar error value=1错误处理
Jmeter是纯Java开发的, 能够运行Java程序的系统一般都可以运行Jmeter, 如:Windows. Linux. mac等. 由于是由Java开发,所以自然需要jdk环境. Windows ...
- Scratch儿童项目式编程—捉迷藏游戏 Scratch children project programming - hide-and-seek game
Scratch儿童项目式编程—捉迷藏游戏 Scratch children project programming - hide-and-seek game 作者:韩梦飞沙 Author:han_me ...