美文网首页
关于游戏SDK ,public.xml 合并的那些事

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

作者: 程某_Fran | 来源:发表于2022-03-02 14:44 被阅读0次

关于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、回编,完成

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

相关文章

  • 修正ApkIdTool

    在上一篇文章关于游戏SDK ,public.xml 合并的那些事[https://www.jianshu.com/...

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

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

  • 《关于游戏那些事》

    一入王者深似海,从此文明是路人。对于这款游戏,我坚持了三年之久,心得颇多,这个游戏坑比真多,若是内心不够强大者,...

  • 关于游戏的那些事

    “四杀!五杀!,赢了!”每天都是这些激昂高亢的呼喊,即使是平日里内向沉默的室友此时也激动无比,无疑这又是一场...

  • 关于游戏的那些事

    为什么玩游戏呢? 没别的原因无聊,玩游戏的时候好像不再那么无聊了。 生活除了吃饭,上课,睡觉,玩手机,好像没有了其...

  • 关于游戏的那些事

    作为一个宅女,视频、游戏、电子书曾经占据了我生活的大部分,那段时光我称之为颓废的那些年,再此并不提倡大家像我一样...

  • 关于游戏的那些事

    昨天和今天都在玩最近很火的一个游戏——阴阳师,游戏还可以吧,不是很合我的口味,只是由此耽误了我这两天的码字,于是由...

  • 关于iOS-SDK那些事

    背景项目构建瘦身注意事项小结 背景 最近一直在负责公司SDK的事宜,随着公司业务的发展,对于有些公司内部可能有许多...

  • iOS SDK开发二三事

    引子 无意间,看到5年前,Android大佬子勰写的,关于SDK开发方面的文章(SDK那些事(总纲)), 不由得唤...

  • 文章收集

    关于视图的周期流程一篇文章揭秘 iOS 布局相关问题 封装SDK流程iOS 自己封装的SDK 打包与合并,新手教程...

网友评论

      本文标题:关于游戏SDK ,public.xml 合并的那些事

      本文链接:https://www.haomeiwen.com/subject/hcbsrrtx.html