API Levels
上面提到的LruCache类是一个很好的例子,为什么你需要知道以哪个API Level为目标。安卓版本大约每6个月发布一次,新的API只在之后的版本有效。任何调用一个不存在的API的试图都会引起crash,不止使用户沮丧同样是开发者的耻辱。比如,调用Log.wtf(TAG, “Really?”)在安卓1.5设备上将会引起应用crash,Log.wtf在安卓2.2开始引入(API 8),这将是可怕的失败。表1-1在不同的安卓版本的性能提升。
Table 1-1 Android Versions
然而,要支持什么样的target,通常不会基于你要使用哪个API,而是你想要达到多大的市场。比如,你的目标是主要的平板而不是手机,你要选择Honeycomb。这样做,你可能限制你的应用的使用者占安卓设备很少的一部分,因为在2011年12月Honeycomb仅占大约2.4%,并不是所有的平板支持Honeycomb。举例来说,Barnes & Noble's Nook使用安卓2.2,而亚马逊Kindle Fire使用安卓2.3),因此,支持早一些的安卓版本仍然有意义。
Android团队理解这个这个问题,他们发布了兼容包,可以通过SDK Updater获取。这个包包含了有一些安卓3.0引入的新的API的静态库,即fragment的API。不幸的是,这个包仅包含了fragment API,而没有其他的在Honeycomb引入的新API。这样的兼容包是个例外,而不是规则。通常,某个API level引入的新API,在比它低的版本里面是不可用的,开发者有责任去仔细的选择API。
为了获取安卓平台的版本号,可以使用Build.VERSION.SDK_INT。具有讽刺意味的是,这个属性在Android 1.6开始引入(API level 4),所以在Android 1.5或者之前的版本试图去通过这种方式获取版本号会导致crash。另外一个选择是Builder.VERSION.SDK,从API level 1开始一直存在。然而,这个属性现在被弃用了,版本字符串不再记录(尽管非常容易理解它们怎么被创建的)。
TIP:通过反射去检查SDK_INT属性是否存在(是否是Android1.6之后的版本)。
Class.forName(“android.os.Build$VERSION”).getField(“SDK”)。
应用的manifest文件需要使用<uses-sdk>标签去指定两个重要事情:
(1) 运行应用所需要的最低API level(android:minSdkVersion)
(2) 应用的目标API level(android:targetSdkVersion)
同样可以指定最高的API level(android:maxSdkVersion),不推荐使用这个属性。指定maxSdkVersion会导致在Andorid系统更新后应用程序被自动卸载。目标API level表示在这个level你的应用被显式的测试过。
默认情况下,Minimum API level会设置为1(表示应用兼容所有的Android版本)。指定一个大于1的API level防止应用被安装到比较老的设备上。比如,android:minSdkVersion=”4”保证使用Build.VERSION.SDK_INT不会有任何的crash风险。minimum API不需要设为应用中使用到的最高API level,只要保证你调用的API确实存在就好了,如Listing 1-12所示。
Listing 1-12 调用Honeycomb(API Level 11)引入的SparseArray的方法
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
sparseArray.removeAt(1); // API 11或者以上
} else {
int key = sparseArray.keyAt(1); // 默认实现会慢一些
sparseArray.remove(key);
}
当尝试去从Android平台获取最好的性能的时候,这种样式的代码更加频繁,因为希望使用最合适的API去完成工作,并且可以在更低版本的设备上面运行(可能需要更低的API)。
Android同样有对其他的事情使用这样的属性,包括决定是否使应用运行在屏幕兼容模式。如果minSdkVersion设为3或者更低,而且targetSdkVersion没有设置为4或者更高,你的应用将运行在屏幕兼容模式。这将防止你的应用程序在平板电脑上全屏显示,比如,使得应用更难使用。平板在最近变得非常流行,很多过去的应用程序还没有更新,所以经常发现某些应用不能在大屏幕设备上合适的显示。
NOTE:Android市场通过minSdkVersion和maxSdkVersion属性去过滤出某个设备可用的应用。其他的类似属性同样用于过滤。同样,Android定义了两个版本的屏幕兼容模式,它们的行为是不同的。关于"Supporting Multiple Screens"完整的描述请参看:http://d.android.com/guide。
除了像在Listing 1-12所示的那样检查version的版本号,也可以通过反射去检测某个特定的方法是否存在。尽管这是一个更加干净和安全的实现方式,反射会使你的代码变慢,因此,需要在对性能要求严格的地方避免使用反射。当性能很重要的时候,一个可用的方式是在静态初始模块里面调用Class.forName() 和Class.getMethod()去判断某个方法是否存在,仅在需要的时候调用Method.invoke( )。
Fragmentation
Android的版本比较多,到目前已经14个版本,使目标市场非常分散,很可能会导致越来越多像1-12所给出的那样的代码。然而,实际上几个Android版本代表了几乎所有的设备。在2011年12月,安卓市场上Android 2.x的版本大约占了多于95%的Android设备。尽管Android 1.6和更早的版本仍然在使用,现在已经不需要花费额外的资源去为这些平台做优化。
运行Android的设备越来越多,当前在www.google.com/phone差不多列出200多款手机,仅在美国就有大约80款。尽管列出的设备都是手机或者平板,他们仍然在很多地方不同:屏幕分辨率,是否存在物理键盘,硬件图形加速器,处理器等。支持不同的配置,或者仅仅一部分,使得应用开发更加有挑战性。因此,为了把你的精力集中在更重要的功能和优化上,对目标市场要有很好的知识。
NOTE:不是所有的Android设备被列在www.google.com/phone上,因为有些国家没有被列出来,比如印度和它的基于安卓2.2的双卡Spice MI270。
Google TV设备(2010年首先由罗技和索尼在美国发布)在技术上和手机、平板没有多少不同。然而,用户和这些设备的交互方式不同。当需要支持这些TV设备的时候,一个挑战是理解你的应用如何在TV上使用。比如,应用程序可以在TV上提供更多的用户体验方式:游戏可以提供多玩家模式,这个功能在手机上没有多少意义。