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系统签名,让应用成为系统级应用。

相关推荐
还鮟2 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡3 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi003 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体
zhangphil5 小时前
Android理解onTrimMemory中ComponentCallbacks2的内存警戒水位线值
android
你过来啊你5 小时前
Android View的绘制原理详解
android
移动开发者1号8 小时前
使用 Android App Bundle 极致压缩应用体积
android·kotlin
移动开发者1号8 小时前
构建高可用线上性能监控体系:从原理到实战
android·kotlin
ii_best13 小时前
按键精灵支持安卓14、15系统,兼容64位环境开发辅助工具
android
美狐美颜sdk13 小时前
跨平台直播美颜SDK集成实录:Android/iOS如何适配贴纸功能
android·人工智能·ios·架构·音视频·美颜sdk·第三方美颜sdk
恋猫de小郭18 小时前
Meta 宣布加入 Kotlin 基金会,将为 Kotlin 和 Android 生态提供全新支持
android·开发语言·ios·kotlin