直接在apk中添加资源的研究
原文 http://blog.votzone.com/2018/05/12/apk-merge.html
之前接手过一个sdk的开发工作,在开发过程中有一个很重要的点就是尽量使用代码来创建控件,资源文件最好放到assets目录下,如果必须使用res资源,需要通过 getResources().getIdentifier("activity_splash","layout", getPackageName())
这种方式来获取资源id,而不能直接通过R文件获取。
今天就来研究一下这个问题。
一、lib项目中r文件中资源唯一标志为static变量
一般的app项目中自动生成的R文件为常量,而在library项目中为变量。根据Android官方文档,在android 14 之后添加的这一特性,之前编译后的lib项目中是常量,之后的为static 变量。 目的是为了在资源冲突时能够修改资源唯一值。 如图在library项目中,自动生成的R文件如下
有一个需要关注的点是R文件是java 代码,在build时会生成.class文件并添加到dex中。 因为R文件中的常量值仅仅受编译器控制,在lib发布之后添加到jar包中的.class并不会受到当前编译器的影响。而通常lib发布之后要给第三方使用。
看一下反编译后的apk
反编译后查看mylib 下对应的R文件smali代码,可见其值又被设置为final(常量)了。 因此我们可以知道编译器在生成apk时虽然没有lib的java代码可以重置和修改,但是在将jar转化为dex时可以转换为常量。 转化为常量后并不影响其使用, 如图在lib中使用layout资源的反编译代码
我们可以看出使用静态常量可以在生成apk时动态修改其指定的值。
如果是常量,编译后v0的值将直接使用常量值,这样修改R.class文件中的值将没有意义。例如在app中反编译后代码如下:
二、从反编译后代结构中查看资源对应问题
可以看到,反编译后的代码与我们写的java代码基本是一一对应的,那么问题来了,Android是怎么通过一个id值来找到需要的资源呢?通过分析Android源码我们当然可以找出过程,但是分析apk反编译后的结构可以给我们更直接的思路。
在res/values/文件夹下有个public.xml文件,其中每行有三对值,分别为type
、name
、id
通过分析我们可以直接得出结论: public.xml中的对应关系直接关系到哪个id找那个资源。
三、添加资源
Unity3d和cocos2d引擎有自己一套方式来添加资源id,假如我们自己搞一个游戏,又想为其添加一些java代码和资源该怎么操作?
根据之前的分析我们可以设计如下测试方案
- 假定我们要添加代码的apk为targetapp,我们编写一个叫mergelib的apk,然后将mergelib中资源和代码添加到targetapp中。
- 在编写mergelib时,使用
getResources().getIdentifier("activity_splash","layout", getPackageName())
的方式获取资源id; - 通过apktool反编译代码,将资源文件复制到要加入的目标apk中;
- 将mergelib的public.xml文件中需要的资源项添加到targetapp中;
- 编译并签名测试。
根据如上实验我们可以确定这样的操作是可行的。测试流程如下:
需要的资源:
- targetapp- 目标app, 在这里代表游戏
- mergelib- 要将其中包含资源的代码合并进去
mergelib 中对资源通过getIdentifier()的方式使用: 例如设置启动页Activity中的ContentView
的设置
int id = getResources().getIdentifier("activity_splash","layout", getPackageName());
setContentView(id);
我们的目标:为targetapp添加一个启动页,启动页代码在mergelib中编写。
执行流程
修改targetapp中AndroidManifest.xml文件
1) 将SplashActivity 的声明添加进去
2) 修改启动Activity复制需要的代码进入targetapp:
复制mergelib中SplashActivity的代码并修改启动MainActivity的启动代码 如图复制资源
1) 将res/anim 下alpha.xml复制到target
2) 将res/layout 下 spalsh.xml复制到target 下修改ids
将 res/values/ids.xml 中多出来的行复制到 对应ids.xml文件中如图, 本例中仅有一个id 即ImageView的id, 因此将该行复制到targetapp 中对应的ids.xml文件中即可 位置不重要
修改public.xml文件 mergelib Splash中用到了
anim
,layout
,id
, 并且间接用到了mipmap
,因此这些对应的值都需要添加到targetapp。观察public.xml的文件结构,可以发现如下特点:
1) 所有同类型(type相同)的id连续
2) 同类型的id 前4字节相同, 如下图 anim 的前四字节0x7f01 与 attr 不同3) 所有id唯一
根据以上三个特点,我们将多出来的id添加到target为了保证唯一且方便修改,我们做了如下替换(右侧为target)一切就绪,编译并安装
四、工具化处理public.xml的替换过程
上述手动测试仅仅只有一个资源id的情况,假如我们加入了一个support包或者其他一些包含资源的包,那么资源数量将会增加到几百个,这样的话手动添加肯定是不行的,我们需要一个脚本工具来实现。
脚本接收两个public.xml格式的文本,并输出一个合并版本。 其中targetapp中的id值是不可变得,在遇到id冲突时,我们改变mergelib的public.xml。
1) 复制mergelib的public文件为mergeid.xml
2) 复制target app 的public 并命名为oriid.xml
3) 将mergeid.xml 和oriid.xml 放到RIDreset.py同目录下, 运行py脚本
4) 出现 oriid.xml_ 文件, 即生成的合并文件
案例及脚本
https://github.com/votzone/DroidCode/tree/master/VotAndroid/Mergeapp
直接在apk中添加资源的研究的更多相关文章
- 使用NetBeans生成jar包,并在jar包中添加资源
在NetBeans中,执行Clean and Build便可得到jar文件 若要在jar中添加资源,先用压缩软件打开jar,然后将资源拖进当前归档文件即可 使用Class.getResource(St ...
- apk中添加第三方so文件
如果你是把so放在libs/armeabi/下,eclipse中so会自动打包进去,然后使用System.load("data/data/xxx.xxx.xxx/lib/xx.so" ...
- QT工程中添加资源(简单明了)
1. 在工程文件下右击添加新文件 2. 在QT目录下选择QT Resource File 3. 填写资源名称 4. 点击完成就可以看到自己建立的资源了 5. 点击右键添加现有文件,找到自己要添加的资源 ...
- Android插件化(三)载入插件apk中的Resource资源
Android载入插件apk中的Resource资源 简单介绍 怎样载入未安装apk中的资源文件呢?我们从android.content.res.AssetManager.java的源代码中发现,它有 ...
- 完美解决SpringMVC中静态资源无法找到(No mapping found for HTTP request with URI)问题
https://blog.csdn.net/kingmax54212008/article/details/79330308 今天遇到一个比较新奇的问题,但是也应该是使用spring MVC框架时由于 ...
- 【初码干货】使用阿里云对Web开发中的资源文件进行CDN加速的深入研究和实践
提示:阅读本文需提前了解的相关知识 1.阿里云(https://www.aliyun.com) 2.阿里云CDN(https://www.aliyun.com/product/cdn) 3.阿里云OS ...
- 34.如何获取app(apk和ipa)中的资源
移动互联网中,主要的两个平台是android和ios,android上文件的安装包是后缀名为apk的文件,ios上文件的安装包是后缀名为ipa的文件,在本文分析一下这两种文件的特点,以及如何用程序去解 ...
- WPF 添加 Resources Dictionary 资源 一般类库项目中无法添加资源文件(ResourceDictionary)
在文件夹或者项目右键-> Add(添加),会弹出可以快捷添加的资源,但是你会发现没有 ResourceDictionary资源可以选择. 解决此问题方法: 第一步:工程->右键->U ...
- 【转】QT中添加的资源文件qrc时的路径问题小结
@2019-06-13 [小记] QT中添加的资源文件qrc时的路径问题小结
随机推荐
- border-radius圆角兼容方案
1.下载ie-css3.htc 2.CSS代码段 box { -moz-border-radius: 15px; /* Firefox */ -webkit-border-radius: 15px; ...
- html5 css选择器 井号, 句点的区别
一.理解CSS的样式组成CSS里的样式表是有规则组成的,每条规则有三个部分组成:1.选择器(如下面例子中的:"body"),告诉浏览器文档的哪个部分受规则影响:2.属性(如实例中的 ...
- Search Insert Position 查找给定元素在数组中的位置,若没有则返回应该在的位置
Given a sorted array and a target value, return the index if the target is found. If not, return the ...
- vue知识day1
HTML语义.CSS:样式 js:行为 jQuery:简化了js操作 boostrap :框架 ,以类方式展现 react:facebook 公司的产品 angular:谷歌公司产品 vue:作者尤雨 ...
- 中间件(WAS、WMQ)运维 9个常见难点解析
本文由社区中间件达人wangxuefeng266.ayy216226分享整理,包括WAS.WMQ在安装.巡检.监控.优化过程中的常见难点. 安装 1.was 负载均衡的机制的粘连性,was负载均衡异常 ...
- mssql修改id
alter table image alter column id int IDENTITY (1, 1) NOT NULL 我只能上查询分析器,所以只 ...
- linux fack 文件系统修复命令
[简介] fsck命令被用于检查并且试图修复文件系统中的错误.当文件系统发生错误四化,可用fsck指令尝试加以修复. [选项]必要参数 -a 非互交模式,自动修复 -c 检查是否存在有损坏的区块. - ...
- 《Java并发编程实战》文摘
更新时间:2017-06-03 <Java并发编程实战>文摘,有兴趣的朋友可以买本纸质书仔细研究下. 一 线程安全性 1.1 什么是线程安全性 当多个线程访问某个类时,不管运行时环境采用何 ...
- Intel酷睿前世今生(二)
上一文,讲述到了酷睿构架的诞生.可以显而易见的知道,酷睿构架其实源于笔记本处理器构架.因为在当年的技术趋势中,因为提升主频而带来的负面影响如发热与高功率已经让普通消费者所不满.然而提升主频并没有提升多 ...
- Python语言特性
1 Python的函数参数传递 看两个例子: a = 1 def fun(a): a = 2 fun(a) print a # 1 a = [] def fun(a): a.append(1) fun ...