频道栏目
首页 > 程序开发 > 移动开发 > Android > 正文
AXML解析
2016-07-07 09:10:15         来源:beyond702的专栏  
收藏   我要投稿

一、简述

AXML即Android Binary XML,是Android应用程序APK包中保存xml文件数据的一种方式,可以减小xml文件的大小。resources.arsc文件是apk的资源索引文件,而xml文件是apk的资源文件,resources.arsc保存了xml中需要用到的资源的索引,它们之间有非常强的依赖关系,apk在执行的时候缺一不可。另外大家在用AXMLPrinter去解码AXML文件时会看见输出中有很多属性的值都是一串数字,很难明白是什么意思,实际上这串数字就是资源的索引id值,需要用这个id去resources.arsc中查找才能得到具体的字符串,知道这个属性的值是@string/XXXX或者@drawable/XXXX等等。

在上面的文章中我介绍了Resources.arsc文件的解析方式,本文我来介绍怎样来解析AXML文件。

 

二、AXML文件格式

AXML文件的格式与resources.arsc的文件格式有点类似,如果你清楚resources.arsc文件格式的话,那学习AXML文件格式就更方便了。AXML文件格式所需要的数据结构和resources.arsc的数据结构在同一个文件中定义,都在系统源码的/frameworks/base/include/androidfw/ResourceType.h中,也用到了chunk的概念,文件是以chunk为单位划分的,每个chunk都有chunk header和chunk body。chunk的类型在ResourceType.h文件中有定义:

 

struct ResChunk_header  
 {  
     enum   
     {  
         RES_NULL_TYPE               = 0x0000,  
         RES_STRING_POOL_TYPE        = 0x0001,  
         RES_TABLE_TYPE              = 0x0002,  

         // Chunk types in RES_XML_TYPE
         RES_XML_TYPE                = 0x0003,  
         RES_XML_FIRST_CHUNK_TYPE    = 0x0100,  
         RES_XML_START_NAMESPACE_TYPE= 0x0100,  
         RES_XML_END_NAMESPACE_TYPE  = 0x0101,  
         RES_XML_START_ELEMENT_TYPE  = 0x0102,  
         RES_XML_END_ELEMENT_TYPE    = 0x0103,  
         RES_XML_CDATA_TYPE          = 0x0104,  
         RES_XML_LAST_CHUNK_TYPE     = 0x017f,

         // This contains a uint32_t array mapping strings in the string  
         // pool back to resource identifiers.  It is optional.
         RES_XML_RESOURCE_MAP_TYPE   = 0x0180,  

         // Chunk types in RES_TABLE_TYPE
         RES_TABLE_PACKAGE_TYPE      = 0x0200,  
         RES_TABLE_TYPE_TYPE         = 0x0201,  
         RES_TABLE_TYPE_SPEC_TYPE    = 0x0202  
     };  
     //当前这个chunk的类型  
     uint16_t type;  
     //当前这个chunk的头部大小  
     uint16_t headerSize;  
     //当前这个chunk的大小  
     uint32_t size;  
 };  

 

代码段1

 

具体格式如下图:

\

图1

可以看到和resources.arsc一样,在文件的头部后面紧接着是一个string pool,存放着文件中需要用到的字符串。后面是一个XMLResourceMap,它的作用是:This contains a uint32_t array mapping strings in the stringpool back to resource identifiers. It is optional.由于我对应用中使用资源文件不太熟悉,所以没看明白,以后明白了再更新,但这个chunk是optional的。

再往后是一个Namespace,个人理解和C里面的namespace一个意思吧,类型是RES_XML_START_NAMESPACE_TYPE,Namespace里面是一个个的Element,类型是RES_XML_START_ELEMENT_TYPE,实际上对应着xml中的一个个tag,每个Element里面还有attribute,每个Element里面还可以有子Element,和xml文件中的标签结构是对应的,后面还有结束标签End Element和End Namespace,后面会举例具体说明。每个tag被描述为一个ResXMLTree_node结构体,定义如下:

 

struct ResXMLTree_node
{
    struct ResChunk_header header;

    // Line number in original source file at which this element appeared.
    uint32_t lineNumber;

    // Optional XML comment that was associated with this element; -1 if none.
    struct ResStringPool_ref comment;
};

 

代码段2

 

 

三、举例

举例就和手把手教你解析Resources.arsc用同一个工程的例子吧,以工程中的AndroidManifest.xml的二进制文件作为分析目标,其原始的内容如下:

 




    
    

    
        
            
                

                
            
        
        
        
            
                
            
        
        
    

 

代码段3

 

 

下面把手把手教你解析Resources.arsc文章中用到的/res/values/strings.xml和R.java对应部分也贴出来:

/res/values/strings.xml内容:

 

  
  
  
    Cert  
    Hello world!  
    Settings  
  
  

 

代码段4

R.java内容:

 

 

public final class R {  
              ...  
    public static final class string {  
                     ...  
        /**  Description of the choose target button in a ShareActionProvider (share UI). [CHAR LIMIT=NONE]  
         */  
        public static final int abc_shareactionprovider_share_with=0x7f0a000c;  
        /**  Description of a share target (both in the list of such or the default share button) in a ShareActionProvider (share UI). [CHAR LIMIT=NONE]  
         */  
        public static final int abc_shareactionprovider_share_with_application=0x7f0a000b;  
        public static final int action_settings=0x7f0a000f;  
        public static final int app_name=0x7f0a000d;  
        public static final int hello_world=0x7f0a000e;  
    }  
                     ...  
}

 

代码段5


 

四、解析二进制AndroidManifest.xml

1. Header Chunk

AXML文件的头部格式如下:

 

struct ResXMLTree_header
{
    struct ResChunk_header header;
};

 

代码段6

很简单,AXML文件二进制内容如下:

 

\

图2

2. String Pool Chunk

字符串池与resources.arsc的字符串池的结构什么的是完全一样的,现实一个ResStringPool_header结构体,然后跟着n个字符串偏移数组,然后是字符串,这里就不多说了。

3. Resource ID数组

这个部分不是必须的,可以没有。二进制内容如下图:

\

图3

可以看到类型是RES_XML_RESOURCE_MAP_TYPE(0x180),ResChunk_header后面跟着11个四字节的id值,这些id值对应Android源码中/frameworks/base/core/res/res/values/public.xml文件中的值,看代码段3的例子中所有tag下用到的属性名在public.xml对应的id都会出现在这个数组里面,比如versionCode、versionName、name、label、icon、theme这些,这里可能表明需要用到系统资源文件,这些id是系统资源的id值,但是具体为什么我还不太清楚。

4. Start Namespace Chunk

整个xml文件是一个namespace的范围,namespace的描述是以一个Start Namespace开始和一个End Namespace结束的,中间有多个Element。Start Namespace这个chunk的header是ResXMLTree_node描述,chunk的body是一个ResXMLTree_namespaceExt结构体来描述,二进制内容如下图:

\

图4

可以看到chunk的类型是RES_XML_START_NAMESPACE_TYPE(0x0100),在原始AndroidManifest.xml文件中的line number是0x2,ResXMLTree_namespaceExt结构体定义如下:

 

struct ResXMLTree_namespaceExt
{
    // The prefix of the namespace.
    struct ResStringPool_ref prefix;

    // The URI of the namespace.
    struct ResStringPool_ref uri;
};

 

代码段7

结合上面数据可以看到prefix在string pool的字符串偏移数组的下标是0xB,uri的下标是0xC,到string pool中去查找得到分别对应字符串“android”和“http://schemas.android.com/apk/res/android”,可以看到与代码段2 AndroidManifest.xml原始内容是相对照的,第二行原始内容:http://schemas.android.com/apk/res/android" ,xmlns就表示这个xml的namespace了,这个namespace是android官方标准定义的,uri和prefix是固定的。整个xml文件是属于android=http://schemas.android.com/apk/res/android这个namespace范围内。

 

5. Start Element Chunk

XML文件中的每个tag在这里被描述为Element,同样有起始tag和结束tag,这里对应Start Element和End Element,Start Element这个chunk的header是ResXMLTree_node来描述,chunk的body是一个ResXMLTree_attrExt,后面跟若干个ResXMLTree_attribute,定义如下:

 

struct ResXMLTree_attrExt
{
    // String of the full namespace of this element.
    struct ResStringPool_ref ns;


    // String name of this node if it is an ELEMENT; the raw
    // character data if this is a CDATA node.
    struct ResStringPool_ref name;
    
    // Byte offset from the start of this structure where the attributes start.
    uint16_t attributeStart;
    
    // Size of the ResXMLTree_attribute structures that follow.
    uint16_t attributeSize;


    // Number of attributes associated with an ELEMENT.  These are
    // available as an array of ResXMLTree_attribute structures
    // immediately following this node.
    uint16_t attributeCount;


    // Index (1-based) of the "id" attribute. 0 if none.
    uint16_t idIndex;
   
    // Index (1-based) of the "class" attribute. 0 if none.
    uint16_t classIndex;
   
    // Index (1-based) of the "style" attribute. 0 if none.
    uint16_t styleIndex;
};


struct ResXMLTree_attribute
{
    // Namespace of this attribute.
    struct ResStringPool_ref ns;
   
    // Name of this attribute.
    struct ResStringPool_ref name;


    // The original raw string value of this attribute.
    struct ResStringPool_ref rawValue;
   
    // Processesd typed value of this attribute.
    struct Res_value typedValue;
};

 

代码段8

ResXMLTree_attrExt描述了这个Element的名字、所属的namespace以及这个Element中包含了几个attribute等相关信息。ResXMLTree_attribute描述了attribute的具体信息。

 

这里看二进制内容:

\

图5

chunk的类型是RES_XML_START_ELEMENT_TYPE(0x0102),attribute的个数是3,offset是0x14,0xFFFFFFFF表示null。后面跟着三个ResXMLTree_attribute结构体,第一个name是0x0,从string pool中查找是versionCode,Res_value的类型是TYPE_FIRST_INT,值是0x01,与代码段3中原始内容的android:versionCode="1"是对应的。第二和第三个ResXMLTree_attribute就不分析了。

下面找一个与resources.arsc有关的Element分析一下,二进制内容如下:

\

图6

这个Element的line number是0x0C,name是0x15对应string pool中“application”,与代码段3中我们的例子对应。后面有6个ResXMLTree_attribute,我们看第二个,内容是:0C000000 07000000 FFFFFFFF 08000001 0D000A7F,name是0x07,对应string pool中”label“,Res_value的类型是TYPE_FIRST_INT,值是0x7F0A000D,看过手把手教你解析Resources.arsc这篇文章的人会发现很眼熟,这不就是R.java中为每个资源分配的id值吗?对了,系统会根据这个id值去resources.arsc文件中去找具体的资源内容,方法这里就不说了,可以到我的那篇文章中去看。我们直接到代码段5的R.java中去找这个id,对应的是app_name,再看代码段3中原始的内容是

也就是说在APK中某个AXML文件如果引用到其它AXML文件的资源时,都是通过id来引用的,在解析的时候都需要通过resources.arsc去索引。

6. End Element Chunk

每个tag都有Start Element和End Element来标识这个tag的范围,End Element Chunk的header是由一个ResXMLTree_node来描述,它的body是由一个ResXMLTree_endElementExt来描述,定义如下:

 

struct ResXMLTree_endElementExt
{
    // String of the full namespace of this element.
    struct ResStringPool_ref ns;

    // String name of this node if it is an ELEMENT; the raw
    // character data if this is a CDATA node.
    struct ResStringPool_ref name;
};

 

代码段9

其中namespace和name都是和Start Element对应的,这里就不多说了。

 

7. End Namespace Chunk

End Namespace Chunk的header由ResXMLTree_node来描述,chunk的body由ResXMLTree_namespaceExt来描述,同样内容与Start Namespace Chunk相对应,这里我也不再多说了。

五、总结

大家可以自己写个简单的例子跟着解析一下,很容易就可以明白AXML的格式了,非常容易。当然上面我介绍的不太全面,比如Resource ID以及一些结构的字段都没有详细的介绍,大家可以去ResourceType.h里面去看具体的定义,有一些我也不太明白所以就没敢说,以免误人子弟。由于本人才疏学浅,上述内容如果有错误,麻烦指正,谢谢!

点击复制链接 与好友分享!回本站首页
相关TAG标签
上一篇:Android官方文档之User Interface(Styles and Themes)
下一篇:【Android】ORM数据库框架之GreenDao快速入门与使用
相关文章
图文推荐
点击排行

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站