Resources资源的查找过程

以setContentView为列

1、Activity.setContentView

2、PhoneWindow.setContentView
. LayoutInflater.inflate(layoutResID, mContentParent)

3、 LayoutInflater.inflate
getContext().getResources().getLayout(resource)返回XmlResourceParser,然后调用其成员函数inflate(parser, root, attachToRoot);

4、Resources.getLayout(resourceId)
    loadXmlResourceParser(resourceId, "layout");

5、Resources.loadXmlResourceParser
getValue(id, value, true);得到资源文件的名称
loadXmlResourceParser(value.string.toString(), id, value.assetCookie, type);value 为TypeValue类型。

6、Resources.getValue
mAssets.getResourceValue(id, outValue, resolveRefs); mAssets为AssetManager类型

7、AssetManager.getResourceValue
loadResourceValue(ident, outValue, resolveRefs);返回Int,结果值保持在outValue中,sringBlock数组中的每一个StringBlock对象描述的都是当前应用程序使用的每一个资源索引表的资源项值字符串资源池,当AssetManager类的成员函数loadResourceValue的返回值block大于等于0的时候,实际上就表示参数ident所描述的资源项在当前应用程序使用的第block个资源索引表中。

C++层

8、AssetManager.loadResourceValue
 loadResourceValue()JNI方法,得到C++层的AssetsManager,调用其getResources方法,返回ResTable,资源表, 调用ResTable对象的成员函数getResource来获得与参数ident所对应的资源项值及其配置信息,并且保存在类型为Res_value的变量value以及类型为ResTable_config的变量config中。
 如果参数resolve的值等于true,那么就继续调用上述得到的ResTable对象的成员函数resolveReference来解析前面所得到的资源项值
调用函数copyValue将上述得到的资源项值及其配置信息拷贝到参数outValue所描述的一个Java层的TypedValue对象中去,返回调用者可以获得与参数ident所对应的资源项内容。

9、 AssetManager.getResources
   getResTable(required):返回ResTable
当前应用程序所使用的资源包有两个,其中一个是系统资源包,即/system/framework/framework-res.apk,另外一个就是自己的APK文件。这些APK文件的路径都分别使用一个asset_path对象来描述,并且保存在AssetManager类的成员变量mAssetPaths中。

9.1、检查资源包里面的resources.arsc文件已经提取出来,调用当前正在处理的AssetManager对象的成员变量mZipSet所指向的一个ZipSet对象的成员函数getZipResourceTableAsset就可以获得一个对应的Asset对象,mZipSet所指向的一个ZipSet对象。
9.2、如果资源包里面的resources.arsc文件还没有提取出来,那么就会调用当前正在处理的AssetManager对象的成员函数openNonAssetInPathLocked来将该resources.arsc文件提取出来。
9.3、Asset对象ass添加到变量rt所描述的一个ResTable对象中去,这是通过调用该ResTable对象的成员函数add来实现的

10、ResTable.getResource
参数resID描述的是要查找的资源的ID,ResTable类的成员函数getResource分别获得它的Pakcage ID、Type ID以及Entry ID,保存在变量p、t以及e中。知道了Pakcage ID之后,就可以在ResTable类的成员变量mPackageGroups中找到对应的PakcageGroup

11、ResTable.getEntry

12、ResTable.resolveReference
ResTable类的成员函数resolveReference的实现其实很简单,它就是对参数value所描述的一个资源项值进行解析,前提是这个资源项值是一个引用。一个资源项的值有可能是嵌套引用的,也就是可能是引用的引用,因此,ResTable类的成员函数resolveReference需要使用一个while循环来不断地对参数value所描述的一个资源项值进行解析,直到最后一次解析出来的结果不是引用为止。但是,为了防止无限地解析下去,该while循环最多只允许执行20次。每一次解析都是调用ResTable类的成员函数getResource来实现的,沿着调用路径最终返回到前面的Step 5中,即Resources类的成员函数loadXmlResourceParser中,我们就可以得到参数id所描述的资源项的值了。

13、Resources.loadXmlResourceParser
Resources类最多可以缓存最近读取的四个Xml资源文件的内容,读取超过四个Xml资源文件之后 ,XmlBlock数组和资源ID数组就会被循环利用。
mCachedXmlBlockIds所指向的资源ID数组中检查是否存在一个资源ID与参数id所描述的资源ID相等。如果存在的话,那么就会在成员变量mCachedXmlBlocks所指向的XmlBlock数组中找到一个对应的XmlBlock对象,并且调用这个XmlBlock对象的成员函数newParser来创建一个XmlResourceParser对象返回给调用者。
 如果在Resources类的成员变量mCachedXmlBlockIds所指向的资源ID数组找到对应的资源ID的话,那么Resources类的成员函数loadXmlResourceParser就会调用成员变量mAssets所指向的一个Java层的AssetManager对象的成员函数openXmlBlockAsset来打开参数file所指定的Xml资源文件,从而获得一个XmlBlock对象block。这个XmlBlock对象block以及参数id所描述的资源ID同时也会被缓存在Resources类的成员变量mCachedXmlBlocks和mCachedXmlBlockIds所描述的XmlBlock数组和资源ID数组中。 最后,Resources类的成员函数loadXmlResourceParser就可以调用前面得到的XmlBlock对象block的成员函数newParser来创建一个XmlResourceParser对象返回给调用者。

14、AssetManager.openXmlBlockAsset

15、AssetManager.openXmlBlockAsset
        JNI方法,调用它的成员函数openNonAsset来打开参数fileName所指定的Xml资源文件,接下来就会创建一个ResXMLTree对象,并且将前面所打开的Xml资源文件的内容设置到该ResXMLTree对象中去,并且将该ResXMLTree对象的地址值返回给调用者。

16、AssetManager.openNonAsset
 AssetManager类的成员函数openNonAsset通过参数cookie知道了当前要打开的文件是位于哪个APK文件之后,接着就继续调用另外一个成员函数openNonAssetInPathLocked来打开该文件

17、 AssetManager.openNonAssetInPathLocked
如果参数ap描述的文件路径是一个目录,那么它就会直接将参数fileName所指向的文件名称附加到该目录后面去,然后直接调用另外一个成员函数openAssetFromFileLocked来打开参数fileName所指向的文件。如果打开失败,那么就再假定参数ap描述的文件路径是一个以“.gz“为后缀的压缩包,然后再次调用成员函数openAssetFromFileLocked来打开参数fileName所指向的文件。 如果参数ap描述的文件路径是一个普通文件,那么就意味着参数ap描述的是一个压缩文件,因此,它就会先调用成员函数getZipFileLocked来打开该压缩文件,然后再调用成员函数openAssetFromZipLocked来在该压缩包将参数fileName所指向的文件提取出来。Android应用程序的资源一般都是打包在一个APK文件里面的,而APK文件就是一个Zip格式的压缩包,因此,AssetManager类的成员函数openNonAssetInPathLocked一般就是按照第二种方式来打开参数fileName所指向的文件。

18、AssetManager.openAssetFromZipLocked
就是从参数pZipFile所描述的压缩包中将参数entryName所描述的文件提取出来,并且根据提取出来的内容保存在一个Asset对象中返回给调用者。

19、 XmlBlock.newParser
 XmlBlock类的成员函数mNative指向的是C++层的一个ResXMLTree对象,调用JNI方法nativeCreateParseState,用来在C++层创建一个ResXMLParser对象,最后再将该C++层的ResXMLParser对象封装成Java层的一个Parser对象中,并且将该Parser对象返回给调用者

20、 LayoutInflater.inflate
 LayoutInflater类的成员函数inflate主要负责处理前面所打开的Xml资源文件的根节点,然后再调用另外一个成员函数rInflate来处理根节点的子节点。每一个节点都表示一个UI控件,这个UI控件是通过调用LayoutInflater类的成员函数createViewFromTag来创建的。

21、LayoutInflater.createViewFromTag
LayoutInflater类的成员函数createViewFromTag接下来检查成员变量mFactory的值是否不等于null。如果不等于null的话,那么它就会指向一个Factory对象,该Factory对象描述的是一个UI控件创建工厂,专门用来负责创建UI控件。
如果LayoutInflater类的成员变量mFactory的值等于null,那么LayoutInflater类的成员函数createViewFromTag就会调用成员函数onCreateView或者createView来创建由参数name所指定的UI控件,取决于参数name是否包含了一个“.”字符。注意,如果参数name是否包含了一个“.”字符,那么就说明当前所创建的UI控件是一个用户自定义的UI控件,也就是不是Android提供的标准控件。

22、 PhoneLayoutInflater.onCreateView
  PhoneLayoutInflater类的成员函数onCreateView只负责创建两类标准的UI控件,一种是属于android.widget包的,另一种是属于android.webkit包的,其中,优先创建android.widget包的UI控件。
 如果参数name所描述的UI控件既不属于android.widget包的,也不属于android.webkit包的,那么 PhoneLayoutInflater类的成员函数onCreateView就会将创建UI控件的操作交给父类来处理,即通过调用父类的成员函数onCreateView来创建。
如果参数name所描述的UI控件是属于android.widget包或者android.webkit包的,那么PhoneLayoutInflater类的成员函数onCreateView就会直接调用父类LayoutInflater的成员函数createView来创建参数name所描述的UI控件,因此,接下来我们就继续分析LayoutInflater类的成员函数createView的实现。

23、 LayoutInflater.createView

你可能感兴趣的:(Resources资源的查找过程)