Android 12系统源码_RRO机制(一)Runtime Resource Overlay机制实践

前言

Android的RRO(Runtime Resource Overlay)机制允许开发者在运行时替换或重写系统资源,例如布局、图标、字符串等。这个机制的目标是为了支持设备定制和主题化,特别是在不修改系统源代码的情况下。RRO通过在系统的资源上叠加一个额外的资源层,来实现个性化和品牌定制,而不需要修改原有的资源文件。通常,RRO被用于OEM厂商在其设备上定制UI和功能,或者在Android版本升级时保持兼容性。

RRO的工作原理是,通过在运行时将资源包(如APK文件)作为叠加层加载到系统中。这样,当应用请求某个资源时,Android首先检查叠加层中的资源,如果存在,则使用叠加层的版本,而不是默认的系统资源。RRO机制的优点是它减少了系统定制的复杂性,同时避免了对基础系统文件的修改。

这种机制最早是在Android 6.0(Marshmallow)中引入的,并且随着Android版本的升级得到了优化和增强。

一、RRO 换肤实践

1.1 创建目标应用

首先新建一个需要被换肤的应用工程RROApplication,该工程中只有一个简单的MainActivity,加载了一个activity_main.xml的布局文件,有三个TextView,引用了三个颜色资源,我们的目标是利用RRO机制替换这三个TextView的颜色。这个工程所产生的应用我们将其称为【目标应用】。

1.2 创建资源包应用

1.2.1 新建资源包应用工程

再次新建一个独立的app工程,这个应用存在的意义就是放置【目标应用】的资源文件,它虽然是一个应用工程,但是不包含任何逻辑和业务,就只是用于存放资源文件。这个工程所产生的应用我们将其称为【资源包】。

1.2.2 配置 AndroidManifest.xml

xml 复制代码
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.rro.overlay">

    <overlay
        android:targetName="ThemeResources"
        android:priority="1"
        android:targetPackage="com.example.rro" />

    <application android:hasCode="false"/>

</manifest>

如果某个APK 的AndroidManifest.xml中包含 标记作为 标记的子项,该APK将被视为『资源包』 。

-【必需设定】android:targetPackage 用于指明 RRO 想要替换的『目标应用』。

-【必需设定】android:hasCode必须设定为 false。由于无法替换代码,因此 RRO 无法使用 DEX 文件。

-【非必需设定】android:targetName 用于指明 RRO 『目标应用』的可替换资源子集的名称。如果『目标应用』没有定义可替换资源集,此属性就不需要设定。

1.3 可替换资源集标签overlayable

1.3.1 目标应用使用overlayable标签标记允许被替换的资源

在 Android 10 或更高版本中,『目标应用』可以使用 标签公开一组允许 RRO 替换的资源,未被公开的资源,则不允许 RRO 替换。

xml 复制代码
<resources>
    <overlayable name="ThemeResources">
        <policy type="public">
            <item type="color" name="purple_200" />
            <item type="color" name="purple_500" />
            <item type="string" name="main_content" />
        </policy>
    </overlayable>
</resources>

一个 APK 可以定义多个overlayable 标签,但每个标签必须在该软件包中具有唯一的名称

1.3.2 资源包指定目标应用中允许被替换的标签

xml 复制代码
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.rro.overlay">

    <overlay
        android:targetName="ThemeResources"
        android:priority="1"
        android:targetPackage="com.example.rro" />

    <application android:hasCode="false"/>

</manifest>

当目标应用定义 标签的时候,资源包的需满足以下条件:

  • 必须指定 targetName
  • 只能替换 标记中列出的资源。
  • 只能定位到一个 名称。

1.3.3 限制策略

使用标记可以在目标应用中对可替换资源施加限制。type属性指定叠加层必须满足哪些政策的要求才能替换包含的资源。支持以下类型:

  • public:任何叠加层均可替换相应资源。
  • system:系统分区上的任何叠加层均可替换相应资源。
  • vendor:vendor 分区上的任何叠加层均可替换相应资源。
  • product:product 分区上的任何叠加层均可替换相应资源。
  • signature:使用与目标 APK 相同的签名进行签名的任何叠加层均可替换相应资源。
xml 复制代码
<overlayable name="ThemeResources">
   <policy type="vendor" >
         <item type="string" name="main_content" />
   </policy>
   <policy type="product|signature"  >
        <item type="color" name="purple_200" />
   </policy>
</overlayable>

如需指定多个政策,请使用竖线 (|) 作为分隔符。 如果指定了多个政策,叠加层只需满足一个政策的要求即可替换 标记中列出的资源。

1.4 定义资源映射

1.4.1 Android 10或以下版本

在 Android 10 或更低版本中,系统是根据资源的名称进行资源替换,所以我们只需要在资源包中将需要替换的资源定义好即可。

1.4.2 Android 11或以上版本

在 Android 11 或更高版本中,Google推荐在『资源包』的res/xml 目录中创建一个文件overlays.xml,枚举出应覆盖的『目标应用』的资源值及其替换值。

注意,target标签中的color并没有带上@标记,他实际上仅仅是一个字符串而不是引用

然后在资源包的AndroidManifest.xml 中将android:resourcesMap 属性的值设置为资源映射文件。

xml 复制代码
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.target.overlay.eleven">

    <overlay
        android:targetName="ThemeResources"
        android:targetPackage="com.android.target"
        android:resourcesMap="@xml/overlays"/>

    <application
        android:hasCode="false"/>

</manifest>

二、RRO换肤效果展示

2.1 构建安装包

在AndroidStudio中分别编译构建目标应用安装包和资源包应用安装包

2.2 安装目标应用

在我们安装完目标应用RROApplication-debug.apk之后,打开该应用,默认的显示效果如下所示。

2.3 安装资源包应用

在我们安装完资源包应用RROResource-debug.apk之后,重新打开目标应用,会发现目标应用并不会发生什么变化,这是为什么呢?其实这是因为资源包应用的overlay功能开关默认没有被开启所导致的。

通过执行adb shelldumpsys overlay com.example.rro.overlay这条指令,可以得到以下关键信息。

java 复制代码
com.example.rro.overlay:0 {
  mPackageName...........: com.example.rro.overlay
  mOverlayName...........: null
  mUserId................: 0
  mTargetPackageName.....: com.example.rro
  mTargetOverlayableName.: ThemeResources
  mBaseCodePath..........: /data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk
  mState.................: STATE_DISABLED
  mIsEnabled.............: false//注释1
  mIsMutable.............: true
  mPriority..............: 2147483647
  mCategory..............: null
  mIsFabricated..........: false
}
IDMAP OF com.example.rro.overlay
Paths:
    target path  : /data/app/~~B-NqptfXHZZHKfqYzQu-tg==/com.example.rro-8BHPMbRD_6ZPxdM6ZkYu3Q==/base.apk
    overlay path : /data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk
Debug info:
    W overlay '/data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk' is not allowed to overlay resource 'color/purple_700' in target: target resource has no overlayable declaration
    W overlay '/data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk' is not allowed to overlay resource 'color/teal_200' in target: target resource has no overlayable declaration
    W overlay '/data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk' is not allowed to overlay resource 'color/teal_700' in target: target resource has no overlayable declaration
    W overlay '/data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk' is not allowed to overlay resource 'color/black' in target: target resource has no overlayable declaration
    W overlay '/data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk' is not allowed to overlay resource 'color/white' in target: target resource has no overlayable declaration
Mapping://注释2
    0x7f05004a -> color 0xff000000 (color/purple_200)
    0x7f05004b -> color 0xff000000 (color/purple_500)
    0x7f0d001d -> string "RRO替换资源" (string/main_content)

在注释1处可以发现该资源包的overlay功能并没有被开启,因此目标应用对应的UI样式才没有发生变化。

在注释2处可以发现该资源包所要替换的具体资源条目信息。

2.4 为资源包应用开启overlay功能开关

执行cmd overlay enable com.example.rro.overlay可以为资源包开启overlay功能开关

然后再执行adb shelldumpsys overlay com.example.rro.overlay这条指令,可以得到以下关键信息。

java 复制代码
dumpsys overlay com.example.rro.overlay
com.example.rro.overlay:0 {
  mPackageName...........: com.example.rro.overlay
  mOverlayName...........: null
  mUserId................: 0
  mTargetPackageName.....: com.example.rro
  mTargetOverlayableName.: ThemeResources
  mBaseCodePath..........: /data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk
  mState.................: STATE_ENABLED
  mIsEnabled.............: true//注释1
  mIsMutable.............: true
  mPriority..............: 2147483647
  mCategory..............: null
  mIsFabricated..........: false
}
IDMAP OF com.example.rro.overlay
Paths:
    target path  : /data/app/~~B-NqptfXHZZHKfqYzQu-tg==/com.example.rro-8BHPMbRD_6ZPxdM6ZkYu3Q==/base.apk
    overlay path : /data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk
Debug info:
    W overlay '/data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk' is not allowed to overlay resource 'color/purple_700' in target: target resource has no overlayable declaration
    W overlay '/data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk' is not allowed to overlay resource 'color/teal_200' in target: target resource has no overlayable declaration
    W overlay '/data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk' is not allowed to overlay resource 'color/teal_700' in target: target resource has no overlayable declaration
    W overlay '/data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk' is not allowed to overlay resource 'color/black' in target: target resource has no overlayable declaration
    W overlay '/data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk' is not allowed to overlay resource 'color/white' in target: target resource has no overlayable declaration
Mapping:
    0x7f05004a -> color 0xff000000 (color/purple_200)
    0x7f05004b -> color 0xff000000 (color/purple_500)
    0x7f0d001d -> string "RRO替换资源" (string/main_content)

可以发现注释1处,该资源包的overlay功能开关已经被开启,此事目标应用的显示效果也会发生变化。

三、启用/停用RRO功能

3.1 使用adb指令

java 复制代码
adb shell cmd overlay enable [com.example.rro.overlay] // 开启
adb shell cmd overlay disable [com.example.rro.overlay] // 关闭

使用以上指令可以为特定包名的资源包开启或者关闭overlay功能。

3.2 使用系统api

java 复制代码
OverlayManager overlayManager = context.getSystemService(OverlayManager.class);
OverlayInfo overlayInfo = manager.getOverlayInfo("com.example.rro.overlay", UserHandle.CURRENT_OR_SELF);
if(overlayInfo != null){
	//true为开启,false为关闭
	overlayManager.setEnabled("com.example.rro.overlay", true, UserHandle.CURRENT_OR_SELF);
};

可以在系统源码中直接使用OverlayManager提供的API可以启用、停用RRO,但是OverlayManager在公开的Android SDK中并没有提供,如果是使用Gradle构建工程,需要额外使用AOSP源码编译一个framework.jar并引入才可以使用,同时应用签名也需要使用Android系统签名,让应用成为系统级应用。

相关推荐
编程、小哥哥30 分钟前
python操作mysql
android·python
Couvrir洪荒猛兽1 小时前
Android实训十 数据存储和访问
android
五味香3 小时前
Java学习,List 元素替换
android·java·开发语言·python·学习·golang·kotlin
十二测试录4 小时前
【自动化测试】—— Appium使用保姆教程
android·经验分享·测试工具·程序人生·adb·appium·自动化
Couvrir洪荒猛兽5 小时前
Android实训九 数据存储和访问
android
aloneboyooo6 小时前
Android Studio安装配置
android·ide·android studio
Jacob程序员6 小时前
leaflet绘制室内平面图
android·开发语言·javascript
2401_897907867 小时前
10天学会flutter DAY2 玩转dart 类
android·flutter
m0_748233647 小时前
【PHP】部署和发布PHP网站到IIS服务器
android·服务器·php
Yeats_Liao8 小时前
Spring 定时任务:@Scheduled 注解四大参数解析
android·java·spring