|

楼主 |
发表于 2012-5-16 14:31:52
|
显示全部楼层
当应用程序在Activity里面调用setContentView(int resourceID)的时候,Android系统是如何为应用加载不同的资源文件完成适配的?
下面是java层的调用链:
Activity.setContentView(int resourceID) -> PhoneWindow.setContentView(int resourceID) -> LayoutInflater.inflate(int resource, ViewGroup root) -> LayoutInflater.inflate(int resource, ViewGroup root, boolean attachToRoot) -> Resources.getLayout(int id) -> Resources.loadXmlResourceParser(int id, String type) -> Resources.getValue(int id, TypedValue outValue, boolean resolveRefs) -> AssetManager.getResourceValue(int ident, int density, TypedValue outValue, boolean resolveRefs) -> AssetManager.loadResourceValue(int ident, short density, TypedValue outValue, boolean resolve)
在上面的掉用链中:
1. 最后加载的是哪个xml是由Resources.getValue(int id, TypedValue outValue, boolean resolveRefs)调用完成之后的outValue.string决定的,因为outValue.string的值就是你的资源文件的具体路径, 如:
1) xxx/values/xxx.xml
2) xxx/layout-sw600dp/xxx.xml
2. AssetManager.loadResourceValue()调的是frameworks/base/core/jni /android_util_AssetManager.cpp里面的native方法, 如何获得正确的outValue值,在native方法俩面主要有一下几步:
1) 调用frameworks/base/libs/utils/ResourceTypes.cpp 的ResTable::getResource(),遍历所有资源文件
2) 在ResTable::getResource()里面调用ResTable::getEntry()来确定资源文件来自哪个entry,即 layout,或者layout-sw<N>dp,由此可见,ResTable::getEntry()是我们这个问题的关键。
3) 在ResTable::getEntry()里面:
a) 首先获取本设备的configurion信息
b) 根据得到的本设备的configurion信息,过滤掉不适应本设备的resource,比如设备是800x480的,那么超过此分辨率的资源 (例:layout-sw600dp)就要被过滤掉,实现在frameworks/base/include/utils /ResourceTypes.h中ResTable_config的match函数中
c) 多过滤后的resource进行最佳适配,找到最符合的resource文件。因为之前已经将不符合的,即大分辨率的resource过滤掉了,所以这里 就找剩下的最大的就可以了。实现在frameworks/base/include/utils/ResourceTypes.h中 ResTable_config的isBetterThan()函数中。
在Android系统里面,平板相关的UI一般都在layout-sw<N>dp里面,这里的N有一般是600或720。表示要求设 备窄边的分辨率不小于600dp.
为使3Q显示平板的UI,我们尝试着将向面b)中过滤resource时,与resoure资源相比的 smallestScreenWidthDp值有设备自身的值强制改成了600,编译过后刷进手机进行尝试,是可以工作的。
Before:
if (smallestScreenWidthDp != 0
&& smallestScreenWidthDp > settings.smallestScreenWidthDp) {
return false;
}
After:
if (smallestScreenWidthDp != 0
&& smallestScreenWidthDp > 600) {
return false;
} |
|