解析二进制resources.arsc文件

414 阅读4分钟

之前有一篇介绍解析及精简xml文件的文章,仅覆盖了protobuf格式,对于二进制格式没有深究。 # android apk二进制资源优化

今天继续实现解析二进制resources.arsc文件。

在实现之前,看了官网的格式定义及一些分析文章,考虑到这个是固定格式,查看了一些demo后直接基于以下demo进行验证学习和完善。
# Android 手把手分析resources.arsc

再次感谢作者的分享!
先放上完整的结构图帮助理解各部分内容,结合代码更容易理解。

resources.arsc-struct.awebp

优化后的代码地址 gitgub-resourcesParse
主要是补充了解析多个package的情况;下面结合代码与结构图进行比较。

首先定义chunk_header,每个块的前三字段都是固定格式,即{type(2),headerSize(4),contentSize(4)}。可以根据type来决定后面的内容如何解析,使用headerSize可以跳过header解析content,使用contentSize可以跳过整块内容,继续解析下一块内容。

public class ResChunkHeader {

    public short type;

    public short headerSize;

    public int size;

    public int getHeaderSize() {
        return 2 + 2 + 4;
    }
}

chunk的类型定义如下:

    public static final int RES_NULL_TYPE               = 0x0000;
    public static final int RES_STRING_POOL_TYPE        = 0x0001;
    public static final int RES_TABLE_TYPE              = 0x0002;
    public static final int RES_XML_TYPE                = 0x0003;

    // Chunk types in RES_XML_TYPE
    public static final int RES_XML_FIRST_CHUNK_TYPE    = 0x0100;
    public static final int RES_XML_START_NAMESPACE_TYPE= 0x0100;
    public static final int RES_XML_END_NAMESPACE_TYPE  = 0x0101;
    public static final int RES_XML_START_ELEMENT_TYPE  = 0x0102;
    public static final int RES_XML_END_ELEMENT_TYPE    = 0x0103;
    public static final int RES_XML_CDATA_TYPE          = 0x0104;
    public static final int 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.
    public static final int RES_XML_RESOURCE_MAP_TYPE   = 0x0180;

    // Chunk types in RES_TABLE_TYPE
    public static final int RES_TABLE_PACKAGE_TYPE      = 0x0200;
    public static final int RES_TABLE_TYPE_TYPE         = 0x0201;
    public static final int RES_TABLE_TYPE_SPEC_TYPE    = 0x0202;
    public static final int RES_TABLE_LIBRARY_TYPE      = 0x0203;

继续就主要的类型做拆解

类型结构类主要字段
RES_TABLE_TYPE=2ResTableHeader{ResChunkHeader(8),packageCount(4)}
RES_STRING_POOL_TYPE=1未定义{ResStringPoolHeader(28),stringCount4,styleCount4,strings,styles}
RES_TABLE_PACKAGE_TYPE=512ResTablePackage{ResChunkHeader,id,name...} id是resourceId(0xPPTTEEEE)里的PP
RES_TABLE_TYPE_SPEC_TYPE=514ResTableTypeSpec{ResChunkHeader,id,res0,res1,entryCount,List<ResTableType>,specs}
RES_TABLE_TYPE_TYPE=513ResTableType{ResChunkHeader,id,flags,reserved,entryCount,entryStart,ResTableConfig,List<ResTableEntry>}
RES_TABLE_LIBRARY_TYPE=515未处理

整体结构如下
ResTableHeader
ResStringPoolHeader,strings,styles
ResTablePackage
----ResStringPoolHeader,typeStrings
----ResStringPoolHeader,keyStrings
----ResTableTypeSpec
--------ResTableType
------------ResTableConfig,List<ResTableEntry>
----------------ResStringPoolRef,ResValue
--------ResTableType...
----ResTableTypeSpec...
--------ResTableType...
ResTablePackage...
----ResStringPoolHeader,srings
----ResStringPoolHeader,srings
----ResTableTypeSpec...
--------ResTableType...
...

下面是一个简单demo工程的节选解析日志

input file length 1014220
ResTableHeader{chunkHeader=ResChunkHeader{type=table(2), headerSize=12, size=1014220}, packageCount=1}
ResStringPoolHeader{chunkHeader=ResChunkHeader{type=string_pool(1), headerSize=28, size=294688}, stringCount=7884, styleCount=0, flags=256, stringsStart=31564, stylesStart=0}
----start parse string pool
----end parse string pool index=7884
----start parse string style
----end parse string style
ResTablePackage{chunkHeader=ResChunkHeader{type=table_package(512), headerSize=288, size=719520}, id=7f, name=com.barran.androidsample, typeStrings=288, lastPublicType=0, keyStrings=644, lastPublicKey=0, typeIdOffset=0}
ResStringPoolHeader{chunkHeader=ResChunkHeader{type=string_pool(1), headerSize=28, size=356}, stringCount=17, styleCount=0, flags=0, stringsStart=96, stylesStart=0}
----start parse string pool
----end parse string pool index=17
----start parse string style
----end parse string style
ResStringPoolHeader{chunkHeader=ResChunkHeader{type=string_pool(1), headerSize=28, size=177924}, stringCount=4988, styleCount=0, flags=256, stringsStart=19980, stylesStart=0}
----start parse string pool
----end parse string pool index=4988
----start parse string style
----end parse string style
ResTableTypeSpec{chunkHeader=ResChunkHeader{type=table_type_spec(514), headerSize=16, size=192}, id=1, entryCount=44, resTableTypes=[], spec size=44}
ResTableType{chunkHeader=ResChunkHeader{type=table_type(513), headerSize=84, size=964}, id=1, entryCount=44, entriesStart=260, resConfig=ResTableConfig{size=64}, entrys size=44, resTableEntries={size=44,non_null_count=44,1=ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=1}, resValue=ResValue{size=8, res0=0, dataType=3, data=83}}}}
ResTableTypeSpec{chunkHeader=ResChunkHeader{type=table_type_spec(514), headerSize=16, size=148}, id=2, entryCount=33, resTableTypes=[], spec size=33}
ResTableType{chunkHeader=ResChunkHeader{type=table_type(513), headerSize=84, size=744}, id=2, entryCount=33, entriesStart=216, resConfig=ResTableConfig{size=64}, entrys size=33, resTableEntries={size=33,non_null_count=33,1=ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=45}, resValue=ResValue{size=8, res0=0, dataType=3, data=118}}}}
ResTableTypeSpec{chunkHeader=ResChunkHeader{type=table_type_spec(514), headerSize=16, size=4884}, id=3, entryCount=1217, resTableTypes=[], spec size=1217}
ResTableType{chunkHeader=ResChunkHeader{type=table_type(513), headerSize=84, size=45736}, id=3, entryCount=1217, entriesStart=4952, resConfig=ResTableConfig{size=64}, entrys size=1217, resTableEntries={size=1217,non_null_count=1217,1=ResTableMapEntry{parent=ResTableRef{index=0}, count=1, resValues=[ResValue{size=0, res0=0, dataType=1, data=268435464}], size=16, flags=1, key=ResStringPoolRef{index=78}, resValue=ResValue{size=0, res0=0, dataType=0, data=0}}}}
ResTableTypeSpec{chunkHeader=ResChunkHeader{type=table_type_spec(514), headerSize=16, size=28}, id=4, entryCount=3, resTableTypes=[], spec size=3}
ResTableType{chunkHeader=ResChunkHeader{type=table_type(513), headerSize=84, size=144}, id=4, entryCount=3, entriesStart=96, resConfig=ResTableConfig{size=64}, entrys size=3, resTableEntries={size=3,non_null_count=3,1=ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=1295}, resValue=ResValue{size=8, res0=0, dataType=18, data=-1}}}}