精通安卓性能优化-第一章(一)

第一章 优化Java代码

 

许多的安卓应用开发者从以往的经验对Java语言有很好的使用知识。从1995年出道以来,Java已经成为了一种非常流行的编程语言。有些调查指出Java在和其他语言比较的时候失去了光泽,比如Object-C或者C#,另外有些调查把Java列为最流行的语言。自然,随着移动设备的销量超过个人电脑和Android平台的成功(在2011年11月,每天激活700,000部),Java比以往更加流行。

 

开发移动应用和开发PC应用有很大的不同。现在手持设备功能很强大,但是在性能上来说,落后于PC。比如,一些基准测试显示4核的Intel Core i7处理器比Samsung Galaxy Tab 10.1的双核Nvidia Tegra 2处理器快20倍。

NOTE: 对基准测试的结果须持保留态度,因为它们经常测量系统的一部分,不一定代表一个典型用例。

本章说明了如何保证Java应用程序在Android设备上良好运行,不管使用的是否是最新的Android版本。首先,我们来看看Android如何执行你的代码。接着,我们回顾几种技术去优化一个著名的数学序列的实现,包括如何利用Android提供的最新API。最终,我们回顾几种技术去提高应用的响应和更有效的使用数据库。

在开始前,你应该意识到代码优化在应用程序开发中并不是第一优先的。提供良好的用户体验和专注于代码的可维护性应该是你的首要任务之一。实际上,代码优化应该是你最后的工作重点之一,而且甚至可能完全不会成为这个进程的一部分。好的做法可以帮助你达到可接受的性能水平而不需要你回到你的代码去问自己做错了什么,花费额外的时间去修正它。

Android如何执行你的代码

虽然Android开发者使用Java,Android平台不包含用来执行代码的Java虚拟机(VM)。相反,应用被编译成Dalvik字节码,Android使用它的Dalvik VM去执行它。Java代码仍然被编译成Java字节码,但是这些Java字节码紧接着被dex编译器(dx,一个SDK工具)编译成Dalvik字节码。最终,你的应用仅包含Dalvik字节码,而不是Java字节码。

比如,计算第n个Fibonacci(斐波那契)序列值的方法的一个实现在Listing 1-1给出,连同类的定义。Fibonacci序列定义如下:

F0=0

F1=1

Fn=Fn-2+Fn-1  (n>1)

 

Listing 1-1 Fibonacci序列的递归实现

 

public class Fibonacci {
    public static long computeRecursively(int n) {
        if(n>1) return computeRecursively(n-2) + computeRecursively(n-1);
        return n;
    }
}

NOTE: 这里已经做了一个细节的优化,当N等于0或者1的时候会直接返回N,而不是添加另外一个if语句块去判断N是否等于0或者1。

一个Android应用程序通常被称为一个APK,因为它会被编译成一个带APK扩展名的文件(比如,APress.apk),实际上是一个简单的压缩文件。压缩包中的一个文件是classes.dex,包括了应用程序的字节码。Android工具链提供了一个工具, dexdump,可以把字节码(包含在APK中的classes.dex文件中)从二进制格式转换为可读的格式。

TIP:因为APK文件是一个简单的ZIP 压缩包,可以使用通用的压缩工具比如WinZip或者7-Zip去查看APK文件的内容。

Listing 1-2显示了对应的Dalvik字节码。

每一行的第一列的数字指出了代码在文件中的绝对位置,除了第一行(给出了方法的名字),紧跟在这个数字后面是一个或者更多16位的字节码单元,表示操作码助记符和操作码参数(opcode: 操作码;mnemonic:助记符),紧跟在它后面的是代码在方法内的相对位置(相对位置或者标签)。比如,在地址0x00255a的字节码单元3724 1100翻译成“if-le v4, v2, 0012 // +0011”,基本含义是“如果虚拟寄存器v4的存储内容小于或者等于寄存器v2的内容,就跳过17个字节码单元到0x0012”。单词“virtual register”表示这些不是真正的硬件寄存器而是Dalvik虚拟机使用的寄存器。

通常,你不需要去看应用程序的字节码,特别是Android 2.2(Froyo)及以后的版本,因为从Android 2.2引入了Just-In-Time(JIT)编译器。Dalvik JIT编译器把Dalvik字节码编译成native code,运行速度显著提高。JIT编译器(有时候简称JIT)能够显著的提高性能,是因为:

(1) Native代码可以直接被CPU执行,而不需要经过虚拟机解释
(2) Native Code可以针对特定的架构(architecture,这里个人理解应该指CPU架构)优化

Google的基准测试显示Android 2.2的代码运行速度比Android 2.1快2到5倍。尽管具体代码不同结果可能会有所不同,使用Android 2.2和以后的版本你可以预期一个显著的速度提升。

没有JIT编译器存在的Android版本(Android2.1以前包括Android2.1)可能会非常明显的影响到你的优化策略。如果目标设备运行Android 1.5(Cupcake)、Android 1.6(Donut)、Android 2.1(Eclair),很可能你需要更加仔细的去检查你期望或者需要在你的应用程序里面提供什么。而且,运行这些早期版本安卓的设备,没有更晚一些的设备功能强大。尽管市场上Android 2.1及其以前版本的设备在缩减,截止2011年12月仍然有12%左右。可能的优化策略包括:

(1) 完全不优化。应用在老一些的设备上可能运行非常慢。
(2) 设置你的应用需要的最低的API level为8,这样应用仅可以安装在Android 2.2及以后的版本的设备上。
(3) 当JIT编译器不存在,针对老的设备进行优化以提供一个好的用户体验。这可能意味着需要屏蔽掉耗CPU的功能。

TIP:在manifest文件里面使用android:vmSafeMode标签去启用或者禁用JIT编译器。默认它是开启的(如果平台支持)。这个属性在Android 2.2开始引入。

现在是时候将代码在一个实际的平台上运行,看看它如何执行。如果你熟悉递归和Fibonacci序列,你可能猜测它将运行的非常慢。这是正确的。在Samsung Galaxy Tab 10.1上,计算第30个Fibonacci数需要大约370毫秒。禁用JIT编译器,需要大约440毫秒。如果在一个计算器应用中使用这个函数,用户会变得很沮丧,因为结果不能被“立即”计算出来。从用户的角度看,如果可以计算的话,结果应该立即显示,在100毫秒或者更少的时间内。这样的一个响应时间保证非常好的用户体验,所以这是我们的目标。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值