当前位置:操作系统 > 安卓/Android >>

[Andorid应用开发]-(3)性能优化设计

 这篇文章说性能设计,我估摸着有很多童鞋都没看到过原文,这里推荐下,文章来自Android官方,在下载的Android Docs的Dev Guide可以看到。如果你没读过这篇文章,那么我强烈建议去细读它。


一个Android应用程序运行在有着有限的计算能力和存储空间及受限的电池寿命的移动设备上。有鉴于此,该应用程序应该是高效的。即便你的程序看起来运行得“足够快”,电池寿命可能是你想要优化你的应用程序的一个原因。对用户来说电池寿命重要,Android的电池耗尽意味着用户将知道是你的应用程序让电池耗尽的。

请注意,虽然本文主要是讲解微优化的,但是这些优化几乎不会破坏你的应用程序。你首要的任务依然是选择合适的算法和数据结构,但这些不在本文讨论的范围之内。

目录 [隐藏]
1 简介
2 明智地进行优化
3 避免创建不必要的对象
4 性能优化的神话
5 用静态方法比虚方法好
6 不要在类内部使用getter和setter
7 使用Static Final 修饰常量
8 使用for-each语法
9 将与内部类一同使用的变量声明在包范围内
10 避免使用浮点数
11 了解和使用Android提供的类库
12 谨慎使用JNI调用本地方法
13 结束语
 

简介
以下是编写高效代码的两条基本原则:

不要做不必要的事
不要分配不必要的内存
明智地进行优化
这份文档是关于Android平台微优化的,这里假定你已经分析了程序知道了那些代码是需要优化的,并且你已经有了一个方法用于衡量你所做的任何改变产生的结果(好或坏)。你只有这么点工程时间用来投资,重要的是你要知道你明智使用了它们。

(参见#结束语可以了解更多的分析应用程序和编写高效的测试标准的技巧)

同时该文档假定你选择了正确的数据结构和算法,并且考虑到了改变你的程序将带来的性能影响。正确地选用数据结构和算法带来的影响与本文档提供所有建议相比将有很大不同,而预知你的改变带来的性能影响将有助于你切换到更好地实现方案(比起应用代码,这对类库代码更重要些)。

(如果你需要这方面的建议,可以参考Josh Bloch编写书籍 Effective Java, 第47条)

在对你的Android应用程序进行微优化时,你将会遇到一个棘手的问题:你要保证你的应用程序运行在多个硬件平台上。不同版本的虚拟机运行在不同的处理器上其运行速度是不同的。你可以说“设备X比设备Y快/慢F倍”,但这个结论是不可以从一台设备推广到其他设备的。特别的是,你在模拟器上的测试结果并不能告诉你在其他任何设备中的性能怎么样。同样,使用JIT的设备和不使用JIT的设备也有相当大的差异:在使用了JIT的设备上运行得性能“最好”的代码可能并不适用于没有使用JIT的设备。

如果你想知道你的应用程序在指定设备上运行的情况,你需要在该设备上做测试。

避免创建不必要的对象
创建对象并非是免费的。虽然GC为每个线程都建立了临时对象池,可以使创建对象的代价变得小一些,但是分配内存永远都比不分配内存的代价大。

如果你在用户界面循环中分配对象内存,就会引发周期性的垃圾回收,用户就会觉得界面像打嗝一样一顿一顿的。Android2.3引入的并发收集器虽有助于垃圾回收,但不必要的工作还是应该避免。

所以,应尽量避免创建不必要的对象实例。下面的例子将有助你理解这条原则:

如果你有一个函数返回一个String对象,而你确切的知道这个字符串会被附加到一个StringBuffer,那么,请改变这个函数的参数和实现方式,直接把结果附加到StringBuffer中,而不要再建立一个短命的临时对象。
当你从用户输入的数据中截取一段字符串时,尽量使用substring函数取得原始数据的一个子串,而不是为子串另外建立一份拷贝。这样你就有一个新的String对象,它与原始数据共享一个char数组。(后果就是如果你只是用原始输入的一小部分,那么使用这种方式你将一直把它保留在内存里)
一个更极端的例子是,把易做图数组分成多个一维数组。

int数组比Integer数组好,这也概括了一个基本事实,两个平行的int数组比(int,int)对象数组性能要好很多。同理,这试用于所有基本类型的组合。
如果你想用一种容器存储(Foo,Bar)元组,尝试使用两个单独的Foo[]数组和Bar[]数组,一定比(Foo,Bar)数组效率更高。(也有例外的情况,就是当你建立一个API,让别人调用它的时候。这时候你要注重对API借口的设计而牺牲一点儿速度。当然在API的内部,你仍要尽可能的提高代码的效率)
总体来说,就是避免创建短命的临时对象。减少对象的创建就能减少垃圾收集,进而减少对用户体验的影响。

性能优化的神话
我们将在这里澄清这份文档先前版本造成的误导性声明。

在不使用JIT的设备中,调用一个接口的引用会比调用实体类的引用花费更多的时间(举个例子,调用一个HashMap类型的map比调用一个Map类型的map代价要低,即便在这两种情况下,map都是HashMap的实例)。这里并非是先前说的慢2倍,实际上是更像是慢了6%。此外,JIT有效的区分了两者。

在不使用JIT的设备中,访问缓存的局部变量比重复调用该成员变量快大约20%。在使用JIT的设备中,访问局部变量的花费跟访问成员变量的花费是一样的,所以这并不是一个很好的优化措施,除非你觉得这样会使你的代码更容易阅读(这条建议对final、static和final static 修饰的成员变量也是适用的)。

用静态方法比虚方法好
如果你不需要访问一个对象的成员变量,那么请把方法声明成static。使用静态方法调用将快15-20%。这是一个很好的技巧,因为你可以通过方法签名调用该函数而不会改变对象的状态。

不要在类内部使用getter和setter
在很多本地语言如C++中,使用getter(比如:i = getCount())而不是直接访问成员变量(i = mCount)是一种很普遍的用法。在C++中这是一个非常好的习惯,因为编译器能够内联访问,如果你需要约束或调试变量,你可以在任何时候添加代码。

在Android上,这就不是个好主意了。虚方法的开销比直接访问成员变量大得多。设计公共的接口时,可以遵循面向对象的普遍做法来定义getters和setters,但是在一个类内部,你应该直接访问变量。

在没有JIT的设备上,直接访问成员变量比调用一个简单的getter方法快大约3倍。在有JIT的设备上,直接访问成员变量比调用一个简单的getter方法快大约7倍。目前这是Froyo(android2.2)版本上的事实,我们将在后续版本上改善JIT内联getter方法的效率。

使用Static Final 修饰常量
让我们来看看这两段在类前面的声明:

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>方法,因为在成员变量初始化的时候,会将常量直接保存到类文件中。用到intVal的代码被直接替换成42,而使用strVal的会指向一个相对来说代价小的字符串常量,而不是使用成员变量(要注意的是这个优化只适用于基本类型和字符串常量,而不适用引用类型。尽管如此,这依然是一种很提倡的做法,你应该尽可能地把常量声明为static final)。

使用for-each语法
加强的for循环(有时也叫for-each循环)可以用在实现了Iterable接口的集合类型和数组上。对集合来说,一个迭代器意味着可以调用hasNext()和next()方法。在ArrayList中,手写的计数循环比使用for-each循环快大约3倍,但是对其他集合类来说,foreach相当于使用 iterator。

下面展示了遍历一个数组的3种可选用法:

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;
         }补充:移动开发 , Android ,

CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,