对资源应用做了一些学习,准备从三个方面来描述总结:
1. 整体介绍
2. 资源打包
3. 资源配置&加载
1. 整体介绍
1. 资源类型
定义Android资源时,主要有两个目录,一个是assets, 另一个是res目录。
assets目录用于存放一些原始文件,资源打包后这个目录下的文件不会被编译,内部定义的资源也不会分配ID.
res目录存放的文件则会参与编译(res/raw除外),都会被分配资源ID.
针对前面提到的两个目录下的各种资源,最终都是经过AAPT打包到APK中。资源打包时,会生成R.java, 提供给应用程序通过ID访问资源。
res目录下的文件,除了res/raw外的文件都会进行编译,编译成二进制文件输出,这样会加速后续资源的访问。
对于res目录下的资源,由于都分配了ID, 应用程序可以通过资源ID来访问。而对于assets目录下的资源,应用程序通过具体的文件名访问。
1.2 Android显示相关的概念
Android应用开发提供资源时需要能适配Android设备的多样性 。对与Android不同的显示设备,经常使用如下一些概念描述。
分辨率和DP是最常接触使用的,给定如下一些分辨率与DP转换示例:
配置1: 1920 x 1080, 480dpi ==> 640dp x 360dp
配置2: 1280 x 720, 320dpi ==> 640dp x 360dp
配置3: 960 x 600, 160dpi ==> 960dp x 600dp
配置4: 1280 x 800, 160dpi ==> 1280dp x 800dp
2. Android资源打包
Android代码资源分离,采用AAPT单独对资源编译打包
AAPT(Android Asset Packaging Tool)
源码路径:frameworks/base/tools/aapt/
2. 1 资源打包生成文件
资源编译打包后,在APK中有res, assets两个目录,同时有resources.arsc资源索引文件。
Res目录中除了raw目录,其他文件都是编译过的,另外values会打包到resources.arsc中, 所以打包后的res中没有values目录
Resources.arsc是资源索引文件,可以使用Android Studio直接查看, 或者使用aapt l -a > output导出
资源索引表建立ID与资源值/路径的映射关系
对于layout/drawable等资源, ID映射的值是一个全路径文件名。资源查找时,拿到的是一个文件名,Resource Framework会再根据文件名打开对应的文件提供给App
对于string等一些资源,ID映射的直接是一个值 ,因此资源查找时直接获取到App需要的值。
AssetManager基于索引表查找资源
通过Resource ID加载非asset资源
先通过AssetManager查询resources.arsc中资源ID对应的值
ID对应的如果是一个值,例如value/string值,直接返给应用
ID对应的如果是一个文件名, 需要再通过AssetManager打开文件
通过文件名加载asset资源
直接通过AssetManager打开文件
资源打包的最后过程会生成R.java,里面给出了所有资源的ID.
提供给程序指定资源ID获取到对应资源
ID格式4 Byte: [1][1][2] : [package][type][index]
3. 资源配置
关于资源配置,Android developer上有详细的文档说明:
https://developer.android.google.cn/guide/topics/resources/providing-resources.html
Android定义了18个维度的配置限定符, App通过配置限定符来提供备用资源。
资源定义时需要按表中顺序添加限定符,系统加载资源时也是按表中优先级依次适配资源。
3.1 屏幕尺寸Screen Size限定符
此配置符用于为不同屏幕尺寸提供不同资源
屏幕尺寸: 四种通用尺寸:小、正常、 大 和超大
Android早期版本多用于适配平板所需资源,但区分不够精确
使用smallest width, available width, available height替代
3.2 屏幕宽度/高度Available Width /Height限定符
w<N>dp (w720dp, w1024dp)
h<N>dp (h720dp, h1024dp)
应用实际可用空间不是物理屏的size, 需要减去系统永久性UI的空间
竖屏切换时, 可用宽/高会发生转换
系统会使用最接近(但未超出)设备当前屏幕宽度的值
例如系统检测到当前应用可用屏幕宽度为720dp, 则优先匹配w720dp, 如果没有,则查找小于w720dp的配置
Width对layout的影响更大,使用available height的应用相对较少.
3.3 最小宽度Smallest Width限定符
sw<N>dp (sw320dp,w600dp,sw720dp)
Available Width/Heigh中的最小尺寸
不受屏幕旋转影响
系统会使用最接近(但未超出)设备 smallestWidth 的值
w/h/sw都是应用的configuration, 是Activity实际可用的空间
Freeform模式下,应用窗口大小可以自由缩放,所以在缩放过程中会触发configuration change, 重新加载适合的资源.
3.4 屏幕像素密度限制符
应用开发时仅仅使用sw/w/h有时候不够
[1080p 480dpi] 与[720p 320dpi]对应的sw/w/h相同,但可能需要提供不同的图片资源
应用如果只提供sw<N>dp资源,在小尺寸屏幕或者应用窗口被缩小的情况下,系统可能无法查找到合适的sw限定符资源
sw/w/h没有反应出屏幕像素密度
引入像素密度限制付: <N>dpi
如果没有提供最符合当前设备配置的限定符,则系统使用其中最匹配的资源
先大后小的原则匹配
源码中的算法:
configuration < low < high ==> 选择low
configuration > high > low ==> 选择high
low < configuration < high ==> (2 * low – configuration) * high > configuration * configuration ? : low , high
Android系统会根据物理像素密度设定Android系统的像素密度.
3.5 屏幕分辨率限定符
引入screen size的限制符: <N>x<M>
value-1920x1080
适配原则
匹配最接近(但未超出)设备分辨率的值
源码中匹配算法:
去除超出screen size的资源
使用(w+h)最接近当前configuration.(w+h)的资源
screen size与available width/height的比值为dpi/160
3.6 限定符组合使用
很多场景需要使用多种限定符的组合来提供合理的资源,如下是随意列举的一些配置:
w+h:values-w720dp-h400dp;
sw + dpi:values-sw400dp-xxdpi;
dpi + screen size:values-xxhdpi-1920x1080
4. 资源加载实例
4.1 Layout资源加载
加载原则
1. 按Android定义的18个维度按优先级匹配
2. 某一个维度内,则按当前维度的规则
swxxxdp
优先找最匹配的,然后找低于configuration的swxxxdp
不会匹配高于当前configuration的swxxxdp
Density
优先找最匹配的
然后按先大后小的原则匹配
给定一个配置:1920x1080, 屏幕密度:480dpi, 给定如下一些layout,则加载的优先级如下:
layout-sw360dp
layout-sw240dp
layout-xxhdpi
Layout-xxxhdpi
layout-xhdpi
layout-hdpi
layout-mdpi
4.2 Drawable资源加载
与Layout相同的加载原则:先高后低
假设设备xxhdpi:匹配优先顺序为xxhdpi > xxxhdpi> xhdpi> hdpi> mdpi
为了保证视觉一致性,系统会再对图片按比例进行缩放处理
假设设备xxhdpi, 在drawable-mdpi找到图片,系统认为你放在mdpi下的图片可能偏小,所以做了自动放大处理. 放大的倍数为DPI的比值
此例会放大480/160 = 3倍
影响显示效果, 占用内存增大
同样,如果在drawable-xxxdpi找到图片,系统认为图片偏大,所以自动缩小.
缩小480/640 = 0.75
给定设备:1920x1080, 480dpi. 提供图片:270 x 480 (屏幕1/4),放置在如下不同drawable-xxx目录(xxxhdip , xxhdpi, xhdpi, mdpi),将得到不同缩放的视觉效果:
如何保证Drawable的视觉一致性
如果提供多套资源,图片需要遵循缩放比率
如果提供一套资源,尽量提供高密度的资源
图片放大比图片缩小的视觉效果差
4.3 Values目录
其中有dimen等一些与屏幕显示相关的配置
values目录下一般是使用dp进行定义,已经保证了视觉一致性,所以系统不会进行缩放
某些场景希望视觉尺寸不同,则增加values-xxx目录定义新的值.例如物理尺寸大的平板需要更大的UI显示