背景说明

此文章合适游戏发行行业的安卓开发相关人员查阅,当然其他行业的也可以的哈,只是可能有些行业内术语(如:cp)会干扰阅读等,嘻嘻

public.xml这个文件是哪来的?

该文件是apktool在反编译apk时,根据apk包中的resources.arsc文件生成。

public.xml有什么作用

publc.xml是aapt在打包资源时用来固定资源id的,如果资源在public.xml中有对应的id了,那么打包资源时就用已经有的id

在开发中我们通常使用 Resources.getXXX(resid) 来获取某个资源,传入的是 ID 这个 ID 定义在 http://R.XXX 类里面,R 类是编译器自动生成的,打开看知道

其实资源 ID 就是一个常量,对我们引用了某个ID,编译成 APK 之后,这个ID的值就固定了。

而 apktool 在重新打包 apk 的时候会对资源重新编译 (编译成 resources.arsc )

编译资源的时候资源id会重新编排(除public.xml中固定的外),试想假设原包里面 drawable/a.png id=0x7f020003 ,那么重新编排资源ID后就可是 0x7f020004 ;又假设原包 0x7f020004 对应图片 b.png,那你重新打包后的 apk 当显示 a.png 的时候,就会替换成 b.png,这还好不会出错,倘若生成的一个 id 原包不存在,那会导致程序崩溃的!

所以 public.xml 的作用就是把对象资源 ID 写死!

public.xml中的id的格式

共四个字节32位

  • 第一个字节代表PackgeID
  • 第二个字节代表TypeID
  • 后两个字节代表资源值

PackageID:通常系统资源PackageID是01,而我们自己的资源PackageID是7f

TypeID:比如attr为01,string为02。但是并不固定,并不一定attr就是01。但是在public.xml中,同类型的该字节一定是一样的,否则回编译会失败。

R类

R类这里有个知识点library,模块中生成的R类中的成员的值不是常量,不带final。app模块生成的R类的值是常量值。而常量值在java编译时会被优化,最终代码中输出的就是常量值,而不是http://R.id.xxx这样。而library的因为是变量,不会被优化,代码中会保留http://R.id.xxx

R类和public.xml的关系

从本质上讲,其实并没有啥关系。但是由于在代码中我们会使用R.id去查找资源,这就关联上了。如果都用getIdentifier的方式先获取id,那把R类删了也没事。

public.xml打包后对应的就是resources.arsc中的值,而资源值生成Java类,这个类就是R类。也就是说平时使用R类,就是用里面的索引值去到resources.arsc中找到对应资源位置,再去加载。

切包融合过程中R类和public.xml的处理

切包过程中,R类属于代码,采用直接覆盖的方式,但是由于我们生成的R类跟母包的R类其实值会是不同的。

而public.xml是用的cp(游戏的),为什么用cp的?因为cp建立的是app工程,R类是常量值,如果我们把母包中public.xml中已有的值给改了,万一母包中用了,那就gg了

由于R类在library中使用的时候是个变量,保留了http://R.id.xxx这种形式,解决方法就有了,纠正R类中的值跟public.xml对应,这样就能继续愉快的使用http://R.id.xxx了。

我们的切包过程有几个步骤:

  • 反编译母包
  • 合并渠道资源
  • 合并入新sdk的资源

在反编译母包的时候解析public.xml的值,存下来。对应PublicXmlBean类

private void init() {List<Element> elements = mDocument.getRootElement().elements();for (Element element : elements) {String type = element.attribute(TYPE).getStringValue();String name = element.attribute(NAME).getStringValue();String id = element.attribute(ID).getStringValue();Map<String, String> typeMap = mTypeMap.get(type);if (typeMap == null) {typeMap = new HashMap<>();typeMap.put(name, id);mTypeMap.put(type, typeMap);} else {typeMap.put(name, id);}}
}
复制代码

合并渠道资源的时候,将渠道资源中的public.xml(以channelPublic代指)合并到母包的public.xml(以matrixPublic代指)中

合并策略:

a、channelPublic中有,而matrixPublic中没有,增加到matrixPublic中 比如增加如下数据到matrixPublic中

<public type="attr" name="iconSrc" id="0x7f0200a8" />
复制代码

如果该type在matrixPublic中已经存在:

首先要获取到attr在matrixPublic中的PackageId+TypeId。在一个public.xml文件中,同类型比如attr对应的PackageId+TypeId是不能变的,否则回编译失败。因此要添加数据时,数据的PackageId+TypeId需要纠正为matrixPublic的值。

其次资源值,不能和已有的资源值重复,正常情况下public.xml中的值是aapt生成的有序的,这里可以扫描matrixPublic中attr类型值的最大值,然后加一作为新加的iconSrc的id值

如果该type在matrixPublic中不存在(假设母包中matrixPublic中不存在attr类型):

首先要获取类型已经被占用的有哪些,即获取到matrixPublic中的TypeId,正常情况也是有序的,获取出最大的TypeId,加一作为新Type的起始值。赋值给iconSrc的id值

b、channelPublic中有,而matrixPublic中也有的,不需要处理,保留matrixPublic中的值不变

合并入新sdk的资源,在覆盖完R类,后开始纠正R类的值

扫描R类在PublicAndRHelper中

扫描覆盖完R类的smali代码中所有的R类,R$styleable类除外,因为styleable中保存的是一些数组的值,规则不同。

private void scannerRClass(String path) {File smaliFilePath = new File(path);for (File file : smaliFilePath.listFiles()) {if (file.isDirectory()) {scannerRClass(file.getAbsolutePath());} else if(file.isFile()){if (file.getName().equals("R.smali") || file.getName().startsWith("R$")) {//此处过滤掉styleable文件if (!file.getName().endsWith("R$styleable.smali")) {mRClassFileList.add(file.getAbsolutePath());}}}}}
复制代码

针对每一个R类调用纠正R类中方法,纠正R类值在RValueHelper类中 策略:匹配出要纠正的行,获取到type,name。在public.xml中找出对应的值,纠正。

注意这里的纠正不要用replace(oldValue,newValue)这种方式,要用替换行的方式,因为存在新值在R类中也存在后,后续替换出问题。比如a替换成b,b替换成c的情况最终R类中的a和b都被替换成了c。

其次是styleable的处理,当扫描到的R是attr类型的时候,判断是否有styleable类型的存在,如果存在,则缓存下来attr中所做的纠正,用于纠正styleable。

static void handle(String RFilePath, PublicXmlBean publicXmlBean) {File RFile = new File(RFilePath);String RStyleFilePath = "";Map<String, String> cacheMap = null;if (RFile.getName().endsWith("R$attr.smali")) {RStyleFilePath = RFilePath.replace("R$attr", "R$styleable");File RStyleAbleFile = new File(RStyleFilePath);//styleable存在,则把attr文件替换过的值缓存if (RStyleAbleFile.exists()) {cacheMap = new HashMap<>();}}String rFileContent = FileUtil.read(RFilePath);//找到RFile中是属性的每一行ArrayList<String> lines = FileUtil.readAllLines(RFilePath, ".field public static final");String regex = ".field public static final (.*):(.*) = (.*)";for (String line : lines) {Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(line);if (matcher.find()) {String type = RFile.getName().replace("R$", "").replace(".smali", "");String name = matcher.group(1);String resetValue = publicXmlBean.getValue(type, name);if (StringUtils.isEmpty(resetValue)) {resetValue = publicXmlBean.addValue(type, matcher.group(1));}//替换到文件内容中rFileContent = rFileContent.replace(line, ".field public static final " + name + ":" + matcher.group(2) + " = " + resetValue);if (cacheMap != null) {//换过的值缓存起来cacheMap.put(matcher.group(3), resetValue);}}}FileUtil.write(RFilePath, rFileContent);if (cacheMap != null) {//纠正R$styleable的值List<String> styleAbleLines = FileUtil.readAllLines(RStyleFilePath);BufferedWriter bw = null;try {bw = new BufferedWriter(new FileWriter(RStyleFilePath));for (String styleAbleLine : styleAbleLines) {for (String key : cacheMap.keySet()) {if (styleAbleLine.contains(key)) {styleAbleLine = styleAbleLine.replace(key, cacheMap.get(key));}}bw.write(styleAbleLine);bw.newLine();}} catch (IOException e) {e.printStackTrace();} finally {if (bw != null) {try {bw.close();} catch (IOException e) {bw = null;}}}}
}
复制代码

---------------------------------------这是一条分割线---------------------------------------

该篇文章的作者来自37手游技术部的朱晓鑫同学,欢迎读者留言一起探讨安卓技术~~后续也会呈上更多领域的技术文章与各位大牛一起探讨。敬请期待!

c++歌手类代码_安卓资源ID修改-游戏发行-切包过程中的R类和Public.xml相关推荐

  1. 构造方法与重载:定义一个网络用户类,信息有用户 ID、用户密码、 email 地址。在建立类的实例时把以上三个信息都作为构造函数的参数输入

    构造方法与重载:定义一个网络用户类,信息有用户 ID.用户密码. email 地址.在建立类的实例时把以上三个信息都作为构造函数的参数输入, 其中用户 ID 和用户密码时必须缺省时 email地址是用 ...

  2. 理解Lucene索引与搜索过程中的核心类

    理解索引过程中的核心类 执行简单索引的时候需要用的类有: IndexWriter.Directory.Analyzer.Document.Field 1.IndexWriter IndexWriter ...

  3. 二调建设用地地类代码_二调地类代码

    0分 0 21.0KB 2017-09-17 认证考试aphza二调地类代码表. .~ ? 我们|打〈败〉了敌人. ?我们|[把敌人]打〈败〉了. 表A1 土地利用现状分类 一级类 二级类 含义 编码 ...

  4. 微信小程序跳一跳java代码_安卓版微信小程序跳一跳辅助

    今天一上班 被github上的一篇名为<教你用python玩跳一跳>吸引,它的星也瞬间从3400涨到4400 原作者主要就是用python 脚本去 玩微信小程序跳一跳,本次是基于安卓机型配 ...

  5. 如何区分网线是几类的_银行卡分为三类,怎么知道自己的的银行卡是几类卡?...

    自从银行卡分为三类卡后,很多人都有个疑问:自己手里的卡,到底是几类卡? 2016年12月开始,根据中国人民银行规定,一个人在同一家银行只能开一个I类账户,如果已经有了I类账户,新开户时,就是II类或I ...

  6. 安卓修改电池容量教程_安卓手机端修改电池电量图标的教程

    5.修改电量显示为1%,电池图标为原版+数字显示风格.你一定不全知道的秘密,安卓手机拨号键代码大全.在安卓手机拨号面板中输入以下拨号代码就可执行一些鲜为人知的键盘隐藏功能,这些是开发者所熟知的,但普通 ...

  7. java 修改 枚举类字段_枚举枚举和修改“最终静态”字段的方法

    java 修改 枚举类字段 在本新闻通讯中,该新闻通讯最初发表在Java专家的新闻通讯第161期中,我们研究了如何使用sun.reflect包中的反射类在Sun JDK中创建枚举实例. 显然,这仅适用 ...

  8. java愤怒的小鸟代码_用java实现小游戏“愤怒的小鸟”附源码和效果演示

    推荐下我自己的群:6915 72518,不管你是小白还是大牛,小编我都挺欢迎,不定期分享干货,包括我自己整理的一份最新资料和零基础入门教程!,欢迎初学和进阶中的小伙伴 今天跟大家分享一个用纯Java代 ...

  9. skype安卓手机版_安卓:街机游戏大全~手机版

    街机游戏大全~手机版 大家好,我是雨晨~ 给大家带来的 一款游戏叫做 街机游戏大全 ~手机版 目前支持安卓系统 大家快来亲身感受一下吧~~ 游戏介绍 安卓手机街机游戏1500合集,街机模拟器街机游戏可 ...

  10. yolov8 全网唯一 类代码模式 ,方便研究修改,转onnx学习笔记

    目录 一.基本介绍 二.模型构建 把yolov8参数构建方式改为类代码模式,方便修改研究,可以加载官方预训练模型,可以正确推理 作者的推理报错 三.转onnx

最新文章

  1. 外媒:华为有望最早于2021年推出自动驾驶汽车
  2. Re-Located Record in Grid
  3. HUST1024 dance party(最大流)
  4. HTML+CSS做支付表单
  5. python十种日期格式_Python 日期格式相关
  6. 使用Fiddler为满足某些特定格式的网络请求返回mock响应
  7. 用python画个三维地球_如何用Python制作三维散点图?
  8. 再次携号转网_陕西通信管理局:对移动公司拒绝对用户提供携号转网服务的违法行为处罚!...
  9. gitlab ci php 构建,GitLab CI的入门搭建
  10. oracle经典面试题以及解答
  11. http协议-响应和请求
  12. 静态反编译软件:IDA Pro for Mac
  13. pb文件存储成txt, pbtxt文件
  14. FPGA图像音频通信接口:VGA
  15. pgp解密 java_Java PGP加密解密实例教程详细操作步骤
  16. 关于CH552G单片机连接电脑和烧录程序的细节总结
  17. C++右值引用与函数返回值
  18. 数据挖掘机器学习[六]---项目实战金融风控之贷款违约预测
  19. 【CSS】静态螺纹进度条和静态流程布局
  20. 小论文格式要求(2010年版)

热门文章

  1. android: PendingIntent的使用
  2. Android将应用log信息保存文件
  3. 分析一个文本文件中各个单词出现的频率,把频率最高的10个词打印出来
  4. php函数: urlencode
  5. SQL Server 通过发布订阅 实现数据库同步
  6. -seo要掌握的20条网站的准则
  7. Linux学习——操作文件与目录
  8. LVS+Keepalived 高可用环境部署记录(主主和主从模式)
  9. 11.30 如何取得当事人的银行账号?
  10. 如何根据相机的参数知道摄像机的内参数矩阵