[转]Android:高效之Android代码编写

正文转自:http://www.haoni.org/2011/05/29/androidgaoxiaodeandroiddaimabianxie/

当代之手执设备,与其说是电话,更如相同光将在手中的总括机。不过,虽然是“最抢”的手握紧设备,其特性为等到不上同雅一般的台式电脑。

  这便是干什么我们于书写Android应用程序的上假如十分关心效能。这么些设备连从未这快,并且让电池电量的牵制。这意味,设备尚未再多之能力,我们无法不把程序写的玩命有效。

简介

对于占资源的系统,有有限漫长基本规则:

* 不要开不必要之行
* 不要分配不必要的内存

所有下面的情还照这片个条件。

  有些人恐怕就会跳出来,把本节的大部情节归于“草率的优化”(xing:参见[The
Root of All Evil]),
不可否认微优化(micro-optimization。xing:代码优化,相对于社团优化)的确会带动众多题材,诸如无法使还实惠之数据结构和终
法。然而在亲手执设备上,你困难。假使你道Android虚拟机的属性和台式机卓殊,你的程序卓殊有或同样开端便占用了网的上上下下内存(xing:内存
很有点),这会于您的先后慢得如蜗牛一样,更遑论做其他的操作了。

  Android的功成名就依赖让您的次第提供的用户体验。而这种用户体验,部分因让您的顺序是应快捷而活的,仍然应慢而僵化的。因
为所有的主次还运行在和一个设备之上,都于联合,这即便设在平条路上行驶的汽车。而及时首文档就卓殊给公在博驾照此前务必使学之交通规则。倘诺我们还
遵照这一个规则去做,驾驶就会西夏利,不过假若你莫这么做,你可能相会车坏人亡。这即是干吗这么些规范很重中之重。

  当大家开门见山、直击主题在此以前,还得要指示我们一点:不管VM是否协助实时(JIT)编译器(xing:它同意实时地以Java解释型
程序自动编译成本机机器语言,以要程序执行的速更快。有些JVM包含JIT编译器。),下面提到的这多少个条件都是成立的。假使我们发目标完全相同的点滴只方
法,在说施行时foo()比bar()快,那么编译之后,foo()仍然会比bar()快。所以不要寄希望于编译器可以救你的次序。

免起目的

 

  世界上尚无免费的对象。即使GC为每个线程都起了现对象池,可以要创造对象的代价变得有点有,可是分配内存永远都比不分配内存的代价非常。

苟您于用户界面循环中分红对象内存,就谋面掀起周期性的排泄物回收,用户就会看界面像打嗝一样同间断一间断的。

就此,除非必要,应尽量防止尽力对象的实例。上边的例证将帮扶而了然这长达规则:

*
当您自用户输入的数中截取一段落字符串时,尽量使substring函数取得原始数据的一个子串,而不是吗子串此外建立平等客拷贝。这样您尽管发生一个初的String对象,它与原有数据共享一个char数组。
*
倘诺你生一个函数重临一个String对象,而而方便的精晓之字符串会被增大到一个StringBuffer,那么,请转之函数的参数与落实格局,直接拿结果附加到StringBuffer中,而不用再建立一个短暂的临时对象。

一个再度不过的例子是,把多维数组分成多单一维数组。

*
int数组于Integer数组好,这吗包括了一个大旨事实,两只平行的int数组比(int,int)对象往往组性能要好广大。同理,这试用于具有骨干项目标做。
*
假如您想就此同种容器存储(Foo,Bar)元组,尝试用有限单单身的Foo[]数组和Bar[]反复组,一定比(Foo,Bar)数组功用更强。(也闹差的气象,就是当您建一个API,让别人调用它的时节。这时候若而注重针对性API借口的计划性要牺牲一点儿进度。当然在API的其中,你以
要硬着头皮的加强代码的效能)

整来说,就是制止创造短命的旋对象。减弱对象的创设就会裁减污染源收集,进而减弱对用户体验的熏陶。

接纳当地点法

 

当你在拍卖字串的上,不要爱惜使用String.indexOf(),
String.lastIndexOf()等万分实现的不二法门(specialty
methods)。这么些点子都是利用C/C++实现的,比起Java循环快10暨100加倍。

下实类比接口好

 

倘你出一个HashMap对象,你可用她讲明也HashMap或者Map:

Map myMap1 = new HashMap();
HashMap myMap2 = new HashMap();

何人还好啊?

比如传统的视角Map会更好把,因为这样你可以变更他的切切实实贯彻类似,只要这类似继承自Map接口。传统的见地于人情的次是没错的,但是它并无适合嵌入式系统。调用一个接口的援会较调用实体类的援多花同样加倍之光阴。

若HashMap完全符合你的次序,那么以Map就没有呀价值。若是有点地点你莫可以确定,先避免用Map,剩下的交给IDE提供的重构功效好了。(当然公共API是一个差:一个吓的API经常会牺牲局部特性)

之所以静态方法比虚方法好

 

假如您莫需看一个目的的积极分子变量,那么要把措施阐明成static。虚方法执行之再快,因为它们好叫平素调用而不需要一个虚函数表。其它你也可经过讲明展示出此函数的调用不碰面改目的的状态。

不用getter和setter

 

  以广大地点语言如C++中,都会师采用getter(比如:i =
getCount())来避免直接访问成员变量(i =
mCount)。在C++中就是一个非凡好的习惯,因为编译器可以内联访问,假若您得约依然调试变量,你可以当旁时候增长代码。

  于Android上,这虽未是个好主意了。虚方法的开支相比较平昔看成员变量大得差不多。在通用的接口定义中,可以遵循OO的办法定义getters和setters,然而以相似的类吃,你应有直接访问变量。

拿成员变量缓存到地点

 

走访成员变量比看当地变量慢得多,下边一段落代码:

for (int i = 0; i < this.mCount; i++)
dumpItem(this.mItems[i]);

极致好改化这么:

int count = this.mCount;
Item[] items = this.mItems;

for (int i = 0; i < count; i++)
dumpItems(items[i]);

(使用”this”是为着讲明那么些是成员变量)

任何一个一般的标准化是:永远不要当for的老二单标准被调用任何模式。如下边方法所示,在历次循环的时刻还晤面调用getCount()方法,这样做相比较你以一个int先把结果保存起来开销大群。

for (int i = 0; i < this.getCount(); i++)
dumpItems(this.getItem(i));

无异于要您只要数访问一个变量,也极好先为她确立一个地面变量,例如:

protected void drawHorizontalScrollBar(Canvas canvas, int width, int
height) {
if (isHorizontalScrollBarEnabled()) {
int size = mScrollBar.getSize(false);
if (size <= 0) {
size = mScrollBarSize;
}
mScrollBar.setBounds(0, height – size, width, height);
mScrollBar.setParams(
computeHorizontalScrollRange(),
computeHorizontalScrollOffset(),
computeHorizontalScrollExtent(), false);
mScrollBar.draw(canvas);
}
}

此地发出4赖访成员变量mScrollBar,如若用她缓存到地头,4潮成员变量访问就会成为4不好功效还胜似的栈变量访问。

除此以外就是方的参数与地面变量的效能一样。

下常量

 

给我们来探望这简单段子于类似前边的注明:

static int intVal = 42;
static String strVal = “Hello, world!”;

定为这些会合转移一个号称的先导化类的主意,当类第一破为使用的时节是措施会受执行。方法会将42给予给intVal,然后将一个指向类中常量表的援赋给strVal。当未来只要为此到那个价值的时光,会以成员变量表中查找到他们。
下边大家举办些立异,使用“final”关键字:

static final int intVal = 42;
static final String strVal = “Hello, world!”;

本,类不再要艺术,因为当成员变量开头化的时刻,会将常量直接保存到近似公事被。用到intVal的代码被直互换成42,而接纳strVal的会指向一个字符串常量,而非是使用成员变量。

拿一个艺术依然看似注解也”final”不会师带动性能的升级,可是会支援编译器优化代码。举例说,尽管编译器知道一个”getter”方法不谋面受重载,那么编译器会对那一个动内联调用。

汝吗可以以当地变量阐明也”final”,同样,这也无晤面带来性能的晋级。使用”final”只能使本地变量看起还清晰些(不过也暴发来时
候这是必须的,比如在动匿名内部类的时节)(xing:原文是 or you have to,
e.g. for use in an anonymous inner class)

小心谨慎运用foreach

 

foreach可以据此当贯彻了Iterable接口的聚集类型上。
foreach会给那个目的分配一个iterator,然后调用
hasNext()和next()方法。你顶好以foreach处理ArrayList对象,然则本着任何集合对象,foreach万分给以
iterator。

脚展示了foreach一栽而接受之用法:

public class Foo {
int mSplat;
static Foo mArray[] = new Foo[27];

public static void zero() {
int sum = 0;
for (int i = 0; i < mArray.length; i++) {
sum += mArray[i].mSplat;
}
}

public static void one() {
int sum = 0;
Foo[] localArray = mArray;
int len = localArray.length;
for (int i = 0; i < len; i++) {
sum += localArray[i].mSplat;
}
}

public static void two() {
int sum = 0;
for (Foo a: mArray) {
sum += a.mSplat;
}
}
}

当zero()中,每趟循环都会见访问片糟静态成员变量,取得一致赖数组的尺寸。
retrieves the static field twice and gets the array length once for
every iteration through the loop.

当one()中,将富有成员变量存储到地头变量。 pulls everything out into
local variables, avoiding the lookups.

two()使用了当java1.5遭受引入的foreach语法。编译器会将针对反复组的援和多次组的长短保存至地点变量中,这对准访数组元素相当好。但是编译器还会以历次循环中有一个额外的针对性本土变量的贮存操作(对变量a的存取)这样会师比one()多起4个字节,速度而有些放缓有。

综述:foreach语法在运于array时性大好,可是以于此外集合对象时若小心,因为其相会发额外的靶子。

避下枚举

 

枚举变量卓殊有利于,但不幸的凡它会见牺牲执行之进度以及连大幅增文件体积。例如:

public class Foo {
public enum Shrubbery { GROUND, CRAWLING, HANGING }
}

汇合发生一个900字节的.class文件
(Foo$Shubbery.class)。在它被第一次等调整用时,这多少个类会调用开首化方法来准备每个枚举变
量。每个枚举项都相会为声称成一个静态变量,并吃赋值。然后以这么些静态变量放在一个名也”$VALUES”的静态数组变量中。而如此一非凡堆代码,仅仅是为着
使用三单整数。

这样:

Shrubbery shrub =
Shrubbery.GROUND;会滋生一个针对静态变量的援,假设这静态变量是final
int,那么编译器会直接内联这么些常数。

一边说,使用枚举变量能够给你的API更尽善尽美,并能提供编译时的检讨。所以当通常的上你必应该为公共API接纳枚举变量。不过当性能方面抱有限制的时段,你便应该避免这种做法了。

稍加情况下,使用ordinal()方法取得枚举变量的整数值会重复好一些,举例来说,将:

for (int n = 0; n < list.size(); n++) {
if (list.items[n].e == MyEnum.VAL_X)
// do stuff 1
else if (list.items[n].e == MyEnum.VAL_Y)
// do stuff 2
}

替换为:

int valX = MyEnum.VAL_X.ordinal();
int valY = MyEnum.VAL_Y.ordinal();
int count = list.size();
MyItem items = list.items();

for (int n = 0; n < count; n++)
{
int valItem = items[n].e.ordinal();

if (valItem == valX)
// do stuff 1
else if (valItem == valY)
// do stuff 2
}

会使性能拿到部分改进,但立时并无是最终的解决之道。

以和其中类一起以的变量阐明在担保范围外

央圈下边的类定义:

public class Foo {
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);
}

private class Inner {
void stuff() {
Foo.this.doStuff(Foo.this.mValue);
}
}
}

当即其间的首即使,我们定义了一个中间类(Foo$Inner),它需要拜访外部类的私有域变量和函数。那是官的,并且会打印出咱们目的在于之结果”Value
is 27″。

问题是在技术上来讲(在默默)Foo$Inner是一个了独立的类,它使直看Foo的个体成员是非法的。要抢先那些界限,编译器需要生成一组方法:

/*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”方法时,都汇合调用这个静态方法。就是说,上边的代码表达了一个题材,你是在经过接口方法访问
那么些分子变量和函数而非是直调用它们。在前面我们就说了,使用接口方法(getter、setter)比直访问速度要慢。所以是事例就是于一定语
法下边来的一个“隐性的”性能障碍。

经过将内类看的变量和函数讲明由个体范围转呢力保范围,我们可防止那几个题目。这样做得吃代码运行更快,并且制止有额外的静态方
法。(遗憾之是,那多少个地区和法好被同一个包内的任何类直接看,这和经典的OO原则相背弃。由此当您设计公共API的时刻该当心运用这长达优化原则)

免使用浮点数

 

以跑马CPU出现在此之前,游戏设计者做得无比多之即便是整数运算。随着奔腾的来到,浮点运算处理器成为了CPU内置的特征,浮点和整数配合使用,可以吃您的游玩运行得重顺畅。通常在桌面电脑及,你可以自由的施用浮点运算。

可雅遗憾,嵌入式处理器平时没有援助浮点运算的硬件,所有对”float”和”double”的演算都是经软件实现之。一些主干的浮点运算,甚至用阿秒级的年月才能够完成。

依然整数,一些芯片有针对乘法的硬件支撑而缺失针对除法的协理。这种情状下,整数的除法和取模运算也是暴发软件来就的。所以当你于动哈希表或者举办大量数学运算时必假诺小心谨慎。