关于游戏SDK ,public.xml 合并的那些事

关于sdk的那点事,多次在sdk群看到同行讨论R文件。所以写出我的思路,分享一下自己的经验。

那么开始吧,

首先一般发现需要做合并R操作的, 基本上都是遇到了,没有使用”行规“, 动态获取资源id,而是使用R.id的方式

在说明前, 先暂且称 原母包为A包,需要替换的资源包为B包 , 并且需知道 public.xml是用来固定资源id的

那么就有

方式一 同时删除 A与B的 public.xml 资源ID 由AAPT重新生成

方式二 保留A的 public.xml , 删除B的 public.xml 即 B的资源id由aapt重新生成

方式三 保留B的 public.xml ,删除A的 public.xml 即 A的资源id由aapt重新生成

方式四 通过脚本合成 A和B的 public.xml ,并反向修改相关的R.smali文件

然后A和B 获取资源的方式组成有

A和B都使用动态获取资源Id, 上诉4中方法都可以

A使用动态获取资源id,B 使用R.id的方式 方法三,四 可以轻松解决

A使用R.id,B使用动态获取, 方法二,四 轻松解决

A和B都使用R.id , 方法四 轻松秒杀

由上面分析, 基本可以得出 方法四是个万金油的方法, 而且根据描述的理解起来也并不困难,但很多同学到了怎么合并就开始犯愁了,因为手头上拥有的打包脚本并没有做这方面的处理

在做合并前,需要先了解一下

1、public.xml的作用

2、资源 ID

可以得出,

1、public.xml是用来固定资源Id的

2、在构建时,aapt 工具会收集您定义的所有资源(尽管是单独的文件或文件中的显式定义)并为它们分配资源 ID。

资源 ID 是一个 32 位数字,格式为:PPTTNNNN。PP 是资源用于的包;TT 是资源的类型;NNNN 是该类型中资源的名称。对于应用程序资源,PP 始终为 0x7f

TT 和 NNNN 值由 aapt 任意分配——基本上对于每种新类型,都会分配和使用下一个可用数字(从 1 开始);同样,对于类型中的每个新名称,都会分配和使用下一个可用编号(从 1 开始)。

拥有上面的知识后,基本上合并public.xml也就并没有难度了

逻辑可以参考如下

因为PP始终为0x7f

value = TTNNNN , 资源id为 0x7f + value

可以这么表示 [type][name] = value

那么,我们合并逻辑就可以是, 用A包作为母本,B包融合进A包

即 A的public.xml 先固定

然后从B包的public.xml 里面把每个资源id 拿出出来

先判断是否有type, 再判断是否有name 若[type][name] 存在就跳过,若name不存在,就拿type的maxid + 1

python代码如下


def public_merged(self):

        plug_value_path = os.path.join(self._plug_path, "handleres", "values")

        plug_public_file_path = os.path.join(plug_value_path, "public.xml")

        with open(plug_public_file_path, 'r') as xml_file:

            plug_public_xml = ET.parse(xml_file)

        package_public_file_path = os.path.join(self._package_path, "res", "values", "public.xml")

        with open(package_public_file_path, 'r') as xml_file:

            package_xml = ET.parse(xml_file)

        package_unique_id_mapping = {}

        for ele in package_xml.getroot():

            id_type = ele.attrib['type']

            id_name = ele.attrib['name']

            id_value = int(ele.attrib['id'], 16)

            if id_type not in package_unique_id_mapping:

                package_unique_id_mapping[id_type] = {}

                package_unique_id_mapping[id_type]["maxId"] = 0

                package_unique_id_mapping[id_type]["nameList"] = []

            package_unique_id_mapping[id_type]["nameList"].append(id_name)

            if id_value > package_unique_id_mapping[id_type]["maxId"]:

                package_unique_id_mapping[id_type]["maxId"] = id_value

        all_type_max_id = 0

        for type_map in package_unique_id_mapping.values():

            if type_map["maxId"] > all_type_max_id:

                all_type_max_id = type_map["maxId"]

        for ele in plug_public_xml.getroot():

            id_type = ele.attrib['type']

            id_name = ele.attrib['name']

            if id_type not in package_unique_id_mapping.keys():

                package_unique_id_mapping[id_type] = {}

                package_unique_id_mapping[id_type]["maxId"] = 0

                package_unique_id_mapping[id_type]["nameList"] = []

                package_unique_id_mapping[id_type]["nameList"].append(id_name)

                new_all_type_max_id = ((all_type_max_id >> 16) + 1) << 16

                package_unique_id_mapping[id_type]["maxId"] = new_all_type_max_id

                all_type_max_id = new_all_type_max_id

                ele.set("id", ("0x%x" % new_all_type_max_id))

                package_xml.getroot().append(ele)

                continue

            if id_name in package_unique_id_mapping[id_type]["nameList"]:

                continue

            max_id_value = package_unique_id_mapping[id_type]["maxId"]

            new_id_value = max_id_value + 1

            ele.set("id", ("0x%x" % new_id_value))

            package_unique_id_mapping[id_type]["maxId"] = new_id_value

            package_unique_id_mapping[id_type]["nameList"].append(id_name)

            package_xml.getroot().append(ele)

        package_xml.write(package_public_file_path, "UTF-8")

到这里基本上已经把public.xml合并完成了, 但是需要注意的是如果是用R.id 的方式去拿资源id的话, A包是资源ID是正常的, B包由于是合并进A包会导致B包的资源Id需要更新。
思路就是把B包下的smali文件遍历,拿到R$*.xml 然后把里面的资源id更新为合并后的public.xml的资源id
python脚本如下

    def change_smali_with_public_xml(self, smali_path):
        public_xml_path = os.path.join(self._package_path, "res", "values", "public.xml")
        et_public_xml = ET.parse(public_xml_path)
        r_file_name_list = ["R$xml.smali", "R$style.smali", "R$string.smali", "R$raw.smali", "R$layout.smali",
                            "R$id.smali",
                            "R$drawable.smali", "R$dimen.smali", "R$color.smali", "R$attr.smali", "R$array.smali",
                            "R$anim.smali"]
        unique_id_mapping = {}
        public_xml_root = et_public_xml.getroot()

        for ele in list(public_xml_root):
            res_type = ele.get('type')
            res_name = ele.get('name')
            res_id = ele.get('id')
            if res_type not in unique_id_mapping:
                unique_id_mapping[res_type] = {}
            unique_id_mapping[res_type][res_name] = res_id
        for file_name in r_file_name_list:
            file_path = os.path.join(smali_path, file_name)
            dot_index = file_name.index('.')
            res_type = file_name[2:dot_index]
            if os.path.exists(file_path):
                with codecs.open(file_path, 'r', 'utf-8') as f:
                    lines = f.readlines()
                    for line in lines:
                        res_name = get_res_name_for_line(line)
                        if res_name is not None:
                            new_id = unique_id_mapping.get(res_type).get(res_name)
                            if new_id is not None:
                                new_line = update_res_id(line, new_id)
                                old_line_index = lines.index(line)
                                lines[old_line_index] = new_line

                    out_lines = lines

                with codecs.open(file_path, 'w', 'utf-8') as f:
                    f.writelines(out_lines)

主要代码也就这么多了。

可能有的伙伴在已有的基础上加上上述脚本可能不太符合他们的流程。
另外附送其他的思路:
1、把A,B包的public.xml 删除,合并资源
2、重新生成包C
3、C包解包
4、在C包包名下拿到对应新生成的R文件
5、替换到原来A,B包的R文件
6、修改替换后的R文件的路径地址
7、回编,完成

文笔有限,欢迎大家一起探讨。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容