先抛一个问题:现在有一个第三方应用,没有代码,只有编译好的apk,在不去改动这个apk的前提下,如果想改变这个应用中的一些资源显示效果,比如改变一个button的文字,一个imageview的背景,有没有可能做到?
我的答案是有可能做到(废话,做不到的话让我怎么往下写(bian))。直接上栗子,拿蘑菇街的应用来开刀。先看下蘑菇街的部分原生效果图。
红色框中的文字就是我要改变的地方,接下来放改变后的效果图。
注意圈起来的红色部分文字已经改变了,而我并没有去改动mogujie的apk。不要问为啥替换后的文字这么的丧心病狂,我会说我被mogujie伤害过吗?
上面的这个栗子就引出了我要讲的Android 资源替换-----Runtime Resource Overlay(RRO)。RRO是在Android5.0后引入的,它能在 apk 运行时,自动加载需要定制的资源,而不加载原有的资源。mogujie的应用我压根没动,只是当系统在调用mogujie的资源的时候,我给它分配了一个同名的资源,这样系统不会去获取mogujie原生的资源,而获取的是我分配给它的资源。给出了感性的一个解释,下面开始讲解具体的原理,再次之前需要先说明下应用查找资源的过程。
应用通过 getString/getDrawable去调用某个资源时,会将这个resources ID 作为参数传给 framework 层。同一名称但不同状态的 resources ID 是一样的,比如不同分辨率但名称相同的图片分别被放置在了drawable-hdpi/drawable-ldpi/drawable-mdpi下,但在编译时针对该图片生成的resources ID只有一个。framework 层 查 找 资 源 时会使用这个resources ID , 同时还要使 用 当前 系 统 的configuration(分辨率、语言、横竖屏)。通过 resources ID 和 configuration,系统会找到最匹配的资源(如果没有找到,则使用默认的 ),最后将找到的resources ID返回给 apk。整个过程如下图所示。
结合上图可以看出应用只是负责提供资源IDs,系统层会根据当前的语言,横竖屏,分辨率等状态去取适当的资源ID加载。这种不连续性就提供了资源欺骗的可能,RRO发生在系统层检索资源时,应用层使用资源的接口并没有改变,甚至都不知道还有一个跟它同名的资源存在,因此使用RRO更改应用的资源时无需对原来的应用做任何修改。
RRO机制的运用是依靠overlay apk实现的,与普通的apk相比它不包含代码,只有资源。一个overlay apk只能替换一个目标apk的资源,但一个目标apk的资源可以被多个overlay apk更改。在使用RRO机制的前提是必须要知道待替换目标apk中的资源名,比如我要替换mogujie在桌面的应用名称,就要知道这个应用名称是在哪个资源文件中写入的,这得依靠反编译知识,这里不详述了,直接给出
有了这个就可以开始尽情的替换了。下面就是我制作的mogujie overlay 的目录结构。
其中AndroidManifest.xml中的内容如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mogujieoverylay">
<overlay android:targetPackage="com.mogujie" android:priority="1"/>
</manifest>
targetPackage的值必须要与mogujie应用的package值一样(反编译可以获取),priority的值是针对一个目标apk的资源可以被多个overlay apk更改"而设置的一个优先级,值越大优先级越高。
替换的几个字串都是放在values目录下的string.xml中
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">唯品会</string>
<string name="host_tab_triple">不买</string>
<string name="host_tab_follow">别逛</string>
<string name="host_tab_msg">拒聊</string>
<string name="host_tab_my">呵呵</string>
</resources>
这个过程的难度在于反编译后,如何正确的猜测出目标apk中资源的名称,只有拿到了正确的资源名才能保证overlay成功。到此打包编译就生成了一个overlay apk。在罗嗦下打包的apk的方法.1在源码环境下使用Android.mk文件然后进行模块化编译,这个是最方便快捷的,我刚才使用的Android.mk内容如下
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := Mogujieoverlay
LOCAL_SRC_FILES := $(call all-java-files-under,res)
LOCAL_SDK_VERSION = current
LOCAL_PACKAGE_NAME := Mogujieoverlay
include $(BUILD_PACKAGE)
2使用aapt命令打包,然后签名,必须要签名,否则打包生成的apk是无法安装的。
打包命令:
aapt p -f -I [ANDROID_SDK_PATH]/android.jar
-S [OVERLAY_PATH]/res
-M [OVERLAY_PATH]/AndroidManifest.xml
-c [PRODUCT_AAPT_CONFIG]
-F [MODULE_NAME]-overlay.apk
签名命令:
java -jar [PATH]/signapk.jar
[PATH]/platform.x509.pem
[PATH]/platform.pk8
[MODULE_NAME]-overlay.apk [MODULE_NAME]-overlay.apk_signed
由于RRO是在系统层做的,Android出于安全考虑,要想overlay生效必须将生成的overlay apk放到/system/vendor/overlay目录下才会生效。RRO的应用场景主要是在方案开发时用到,上面的例子只是替换了string资源,实际上RRO能支持除了 layout 和 AndroidManifest 外的所有资源定制,在做ROM定制开发时还是非常有用的。曾经测试报了一个google应用字串太长显示不全的问题,但google 应用压根就没有代码,只有apk,当时就是靠RRO来解决该bug的。RRO剥离了代码和资源的共生关系,在方案开发中可以针对不同的显示需求,打包不同的overlay apk,不用每次更改UI资源都去修改原来的应用。
————————————————
版权声明:本文为CSDN博主「思棉叶」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/azhengye/article/details/49050631