使用资源提供最好的设备兼容性
为了让应用程序支持多种设备配置,始终给应用程序使用的每种资源类型都提供默认的资源是至关重要的。
例如,如果应用程序支持几种语言,就要始终包含没有语言和区域限定符的values/目录。如果把所有的字符串文件都放到由语言和区域限定符的目录中,那么当应用程序运行在不支持应用程序提供的语言集的设备上时,就会崩溃。但是,只要提供了默认的values/资源,应用程序就会正确的运行(即使用户不能理解应用程序提供的语言集,也比程序崩溃要好。)
同样,如果基于屏幕的方向,提供了不同的布局资源,那么也应该选择一个方向作为默认布局。例如,不要在layout-land/目录和layout-port目录中分别提供横向和纵向布局资源,而是要保留其中一个作为默认布局,如让layout/目录作为默认横行布局资源的存放地,而layout-port/目录保存纵向布局资源。
提供默认资源是重要的,不仅因为可以让应用程序在没有预计到的设备上运行,而且还因为某些在Android新版本中添加的配置限定符,旧版本不支持。如果使用了新的资源限定符,但是还要维护代码与Android旧的版本的兼容性,那么当应用程序运行在旧的Android版本上时,如果提供默认资源,应用程序就会崩溃,因为使用了新的限定符命名的资源对应用程序不可用。例如,如果minSdkVersion被设置为4,并且限定了所有的可描画资源都是用night模式(night和notnight限定符在API级别8以后才被添加),那么API级别4的设备就不能访问这些可描画资源,应用程序就会因而崩溃。在这种情况中,可以让notnight模式的资源作为默认资源,因此应该在可描画资源的目录名去除notnight限定符,使得资源目录名如:drawable/、drawable-night/。
因此,为了提供最好的设备兼容性,首先应该始终提供默认资源,以便应用程序能够获得正确执行的资源,然后使用配置限定符给特殊的设备配置创建可选的资源。
这种规则对一种情况除外,如果应用的minSdkVersion是4或更大,在用屏幕分辨率限定符提供可选的描画资源时,就不需要默认的可描画资源。因为,即使没有默认的可描画资源,Android也能够在可选的屏幕分辨率之间找到最匹配的资源,并且按照需要缩放位图。但是,对于所有的设备类型,要获取最好的体验,应该为三种类型的分辨率提供可选的资源。如果minSdkVersion小于4(Android1.5或更低的版本),要注意屏幕的尺寸、分辨率、以及外观限定符是不被支持的,因此对于这些版本的平台需要执行另外的兼容性处理。
给Android1.5提供屏幕资源的兼容性
Android1.5及更低版本,不支持下列配置限定符:
分辨率:
ldpi、mdpi、hdpi、nodpi
屏幕尺寸:
small、normal、large
屏幕外观:
long、notlong
这些配置限定符是在Android1.6中被引入的,因此Android1.5(API级别3及更低版本都不支持它们。如果使用这些配置限定符,并且没有提供相应的默认资源,那么Android1.5设备可能使用上述屏幕配置限定命名的任意一个资源目录中的资源,因为它忽略这些限定符,并使用它能够找到的第一个匹配的可描画资源。
例如,如果应用程序支持Android1.5,并且包含了每种分辨率类型的可描画资源(drawable-ldpi/、drawable-mdpi/、drawable-hdpi/),而且没有包含默认的可描画资源(drawable/),那么Android1.5就会使用任意一个可描画资源目录中的资源,这样就会导致用户界面的不理想。
因此在使用屏幕配置限定符时,要提供与Android1.5及以前版本的兼容性:
1. 把中等分辨率、普通的屏幕尺寸以及非长屏幕作为默认资源来提供。
因为所有的Android1.5设备都有中等的分辨率、普通的屏幕尺寸以及非长的屏幕,所以能够把这些类型的资源放到对应的默认资源目录中。例如,把所有的中等分辨率的可描画资源放到drawable/目录中(而不是drawable-mdpi/),把normal尺寸的资源放到对应的默认资源目录中,并且把notlong资源也放到对应的默认资源目录中。
2. 要确保SDK工具的版本时r6或更高的版本。
SDK工具需要修订版本6或更高,因为它包含一个新的打包工具,这个工具能够自动的把相应版本限定符中资源应用于任何Android1.0中不存在资源目录。例如,分辨率限定符是在Android1.6中(API级别4)被引入的,当打包工具遇到这样的目录时,它就会给目录名中添加“v4”,来确保旧版本不使用这些资源(只有API级别4和更高版本支持的限定符中的资源)。这样,通过把中等分辨率资源放到没有mdpi限定符的目录中,它们依然可以被Android1.5访问,并且支持这个分辨率限定符和有中等分辨率屏幕的任何设备也会使用这个默认资源,因为它们跟设备最匹配。
注意:Android的后续版本,如API级别8中引入的旧版本不支持的配置限定符。要提供最好的兼容性,就应该始终给每种类型的资源包含一组应用程序使用的默认资源。
Android是如何查找最佳匹配资源的
当请求一个资源时,Android会根据当前的设备配置在运行时选择可选的资源。要演示Android是如何选择可选资源的,先假设下列每个可描画目录包含了相同图片的不同版本:
drawable/
drawable-en/
drawable-fr-rCA/
drawable-en-port/
drawable-en-notouch-12key/
drawable-port-ldpi/
drawable-port-notouch-12key/
并且假设设备配置如下:
地区=en-GB
屏幕方向 = port
屏幕像素分辨率=hdpi
触屏类型=notouch
主要文本输入方法=12key
通过比较设备配置来选择有效的可选资源:drawable-en-port。
系统使用以下逻辑最终判断要使用的资源:
1. 排除矛盾的设备配置的资源文件。
Drawable-fr-rCA/目录被排除,因为它跟en-GB矛盾
drawable/
drawable-en/
drawable-fr-rCA/
drawable-en-port/
drawable-en-notouch-12key/
drawable-port-ldpi/
drawable-port-notouch-12key/
例外:屏幕像素分辨率限定符即使是矛盾的也不排除在外,即使设备的屏幕分辨率hdpi,drawable-port-ldpi/不被排除是因为在这个时点,每种分辨率都被认为是匹配的。
2. 选取列表2中最高优先级的限定符。(从MCC开始,然后向下移动)。
3. 任何的资源目录包含这个限定符吗?
如果没有,则返回第2步,继续比较下一个限定符(在这个例子中,在比较到语言限定符之前一直回到“no”)。
如果回答“yes”,那么继续到步骤4
4. 消除资源目录名中不包含这个限定符的目录,在这个例子中,系统会排除所有不包含语言限定符的目录:
drawable/
drawable-en/
drawable-en-port/
drawable-en-notouch-12key/
drawable-port-ldpi/
drawable-port-notouch-12key/
例外:如果提问中的限定符是屏幕像素分辨率,Android会选择那个与设备屏幕分辨率最接近的那个选项。通常,Android提供把一个大的原始图片缩小的图片的能力。
5. 返回,重复步骤2、3、4,直到剩下最后一个目录。在这个例子中,屏幕方向是下一个要匹配的限定符,没有指定屏幕方向的资源目录就会被排除:
drawable-en/
drawable-en-port/
drawable-en-notouch-12key/
图2.Android如何查找最佳匹配资源的流程图
最后剩下的目录是drawable-en-port
尽管每个被请求的资源都要执行这个过程,但是系统还是在某些方面做了一些优化。这种优化之一是一旦了解了设备配置,就可以排除不能匹配的可选资源。例如,如果配置语言是英语(“en”),那么任何没要设置为英语语言限定符的资源目录就不会包含在被检查的资源池中。
当基于屏幕尺寸限定符来选择资源时,如果没有更好的匹配资源,系统会使用给比当前屏幕小的屏幕设计的资源。例如,如果需要,大尺寸屏幕会使用普通尺寸屏幕的资源。但是,如果只有比当前屏幕大的资源可用,那么系统就不会使用它们,并且如果没有其他的资源可用,应用程序就会崩溃。例如,如果所有的布局资源都是针对xlarge限定符的,但是设备确实普通尺寸的屏幕,那么应用程序就会崩溃。
注意:限定符的优先级(在表2中)比跟设备完全匹配的限定符的数量更重要。如,上述步骤4中,最后选择的列表上包含了三个完全跟设备匹配的限定符(方向、触屏类型、和输入法),而drawable-en仅有一个参数(语言)匹配。但是由于语言的优先级比其他的限定符要高,所以drawable-port-notouche-12key就被排除了。
已知问题
Andrid1.5和1.6:版本限定符要完全匹配,而不是最佳匹配
正确行为是让系统跟被标记了等于或小于设备平台版本的版本限定符的资源匹配。但是在Android1.5和1.6(API级别3和4),由于有一个Bug,从而导致系统只有在设备版本跟被标记的版本限定符完全匹配的资源才能使用。
解决方案:要遵照这种行为来提供特俗的资源。但是,因为这个Bug是固定在Android1.6以后的版本中有效,所以如果需要在Android1.5,1.6和以后的版本之间区分资源,那么只需要使用一个版本限定符是1.6的资源和一个跟后续所有版本都匹配的资源。这样问题就不会有太大影响。
例如,如果要想在Android1.5、1.6和2.0.1(以及以后更新的版本)上使用不同的可描画资源,那么创建三个可描画目录:drawable/(针对1.5及更低版本)、drawable-v4(针对1.6)、;以及drawable-v6(针对2.0.1和后续版本---版本2.0,即V5不再有效)。