Android源代码定制:MK文件执行顺序|属性覆盖

简介: Android源代码定制:MK文件执行顺序|属性覆盖

前言

Android的构建系统中,有一些机制和工具可以帮助我们实现源代码的定制和优化,例如:

  • mk文件:这是一种用于描述构建规则和依赖关系的Makefile。可以在mk文件中定义一些变量和指令,来控制源代码的编译和打包过程。
  • 属性(sysprop):这是一种用于控制系统行为和配置的系统属性。我们可以在不同的地方定义和赋值属性,来影响系统的运行时状态。

在本文中,我将介绍这些机制和工具的基本概念和用法,并且通过一些实例来展示如何使用它们来实现Android源代码的定制和优化。还将介绍如何调试我们的修改是否生效,以及如何查看我们的修改的详细信息。

1. 如何确定mk文件的执行顺序

在Android的构建系统中,xxx.mk文件是一种用于描述构建规则和依赖关系的Makefile。mk文件可以通过include-include指令来包含其他mk文件,从而形成一个复杂的构建图。mk文件的执行顺序是由构建图中的依赖关系决定的,而不是由文件名或目录结构决定的。

构建系统会从根目录下的build/make/core/main.mk开始执行,然后根据不同的目标平台和产品选择相应的mk文件。例如我选择了rk3568_s作为目标项目,那么构建系统会执行以下mk文件:

  • build/make/core/main.mk
  • build/make/target/product/core.mk
  • device/rockchip/rk356x/rk3568_s/rk3568_s.mk
  • device/rockchip/rk356x/BoardConfig.mk
  • device/rockchip/common/BoardConfig.mk
  • vendor/customize/customize.mk

每个mk文件都可能包含其他mk文件,形成一个嵌套的结构。可以通过在mk文件中添加$(warning)$(info)指令来打印一些信息,从而观察它们的执行顺序。在调试过程中,在rk3568_s.mkcustomize.mk中添加了以下代码:

$(warning Enter rk3568_s > device.mk is included $(TARGET_PRODUCT))
 
ifeq ($(TARGET_PRODUCT),rk3568_s)
$(warning Enter rk3568_s)
endif
$(warning Enter customize > customize.mk is included $(TARGET_PRODUCT))
 
ifeq ($(filter $(TARGET_PRODUCT),rk3568_s_beta rk3568_s),)
# 这里是当TARGET_PRODUCT不是rk3568_s_beta或rk3568_s时执行的代码
else
# 这里是当TARGET_PRODUCT是rk3568_s_beta或rk3568_s时执行的代码
$(warning Enter $(MY_CUSTOM_PATH)/rk3568_s_beta)
endif

这样当我执行lunch rk3568_s-userdebug时,会看到以下输出:

device/rockchip/rk356x/rk3568_s/rk3568_s.mk:17: warning: Enter rk3568_s > device.mk is included rk3568_s
device/rockchip/rk356x/rk3568_s/rk3568_s.mk:20: warning: Enter rk3568_s
vendor/customize/customize.mk:3: warning: Enter customize > customize.mk is included rk3568_s
vendor/customize/customize.mk:12: warning: Enter ./vendor/customize/rk3568_s_beta

这说明了rk3568_s.mk先于customize.mk被执行,并且它们都被正确地识别了目标产品,除了确定顺序外,还可以通过在mk文件中添加$(warning)$(info)指令来打印 来确认有些模块 是否编译,是否进入了我们预期的编译条件内 懂我意思吧 而不是傻傻的改完就刷机验证,很多内容可以在编译期间就可以知道结果了。

2. 如何覆盖PRODUCT_PROPERTY_OVERRIDES属性

在Android中,有一些系统属性(sysprop)是用于控制系统行为和配置的。例如,ro.sf.lcd_density属性用于指定屏幕密度(dpi)。这些属性可以在不同的地方被定义和赋值,例如:

  • 在设备相关的mk文件中,使用PRODUCT_PROPERTY_OVERRIDES += <property>=<value>指令。
  • 在设备相关的Java代码中,使用SystemProperties.set(<property>, <value>)方法。
  • 在设备相关的C/C++代码中,使用property_set(<property>, <value>)函数。
  • 在命令行中,使用setprop <property> <value>命令。

当一个属性在多个地方被定义和赋值时,它们可能会产生冲突和覆盖。为了避免这种情况,Android的构建系统提供了一些规则和工具来检查和处理属性的冲突和覆盖。

属性的赋值可以分为两种类型:强制可选。强制赋值是指在mk文件中直接使用PRODUCT_PROPERTY_OVERRIDES += <property>=<value>指令的赋值,它表示这个属性必须被设置为这个值。可选赋值是指在mk文件中使用PRODUCT_PROPERTY_OVERRIDES += ?<property>=<value>指令的赋值,它表示这个属性可以被设置为这个值,但也可以被其他强制赋值覆盖。

当一个属性有多个强制赋值时,构建系统会检查它们是否有相同的值。如果有相同的值,那么构建系统会忽略可选赋值,并将这个属性设置为这个值。如果没有相同的值,那么构建系统会报错,并提示找到了重复的赋值。

其实我开始是不知道这么个问题的,因为早期调试Android源码的时候 大概记得可以在不同的MK定义PRODUCT_PROPERTY_OVERRIDES 从而形成覆盖属性, 但是根据我调试发现

假设我在Android源码device目录下 有个文件比如device.mk 定义了

PRODUCT_PROPERTY_OVERRIDES += ro.sf.lcd_density=240

然后vendor目录下又有一个自定义的custom.mk 又定义了

PRODUCT_PROPERTY_OVERRIDES += ro.sf.lcd_density=320

编译Android源码最终会编译使用240还是320?我开始理解的以为根据覆盖原则,在vendor/build.prop 应该只存在240 直接编译。。

编译报错:

error: found duplicate sysprop assignments:

ro.sf.lcd_density=240

ro.sf.lcd_density=320

资料显示:既然已经确定了vendor下的custom.mk是最后加载的,那么它的定义会覆盖之前的定义。但从Android 9 开始,构建系统不再允许同一个属性在多个地方被定义,因此会看到这个错误。

所以这就为啥 在device.mkcustom.mk中都使用了强制赋值:

PRODUCT_PROPERTY_OVERRIDES += ro.sf.lcd_density=240
PRODUCT_PROPERTY_OVERRIDES += ro.sf.lcd_density=320

执行make时,会看到以下错误:

error: found duplicate sysprop assignments:
ro.sf.lcd_density=240
ro.sf.lcd_density=320

这是因为构建系统不允许同一个属性有多个不同的强制赋值。

为了解决这个问题,有以下几种方法:

  • 修改其中一个强制赋值为可选赋值:如果想让一个强制赋值可以被另一个强制赋值覆盖,可以在属性名前加上一个问号(?),表示这是一个可选赋值。如果想让device.mk中的赋值可以被custom.mk中的赋值覆盖,可以修改device.mk中的代码为:
PRODUCT_PROPERTY_OVERRIDES += ?ro.sf.lcd_density=240

当执行make时,构建系统会忽略device.mk中的可选赋值,并将这个属性设置为custom.mk中的强制赋值(320)。

  • 修改构建工具来删除重复的强制赋值:如果不想修改mk文件中的代码,也可以修改构建系统中用于处理属性冲突和覆盖的工具。这个工具是一个py脚本,位于build/make/tools/post_process_props.py。它会读取所有定义的属性,并根据它们的类型和值来决定是否删除或保留它们。可以修改这个脚本来实现想要的逻辑。尝试了以下几种修改:
  • 去掉了重复属性的限制:注释掉了报错的代码,使得构建系统不再检查重复属性。这样当你执行make时,构建系统会保留所有定义的属性,并将它们写入到最终生成的build.prop文件中。例如:
rk_android12.0_sdk/out/target/product/rk3568_s/vendor$ grep -rn "ro.sf.lcd_density"
build.prop:91:ro.sf.lcd_density=240
build.prop:156:ro.sf.lcd_density=320
  • 这样做的问题是,当系统启动时,它会读取最后一个定义的属性作为最终的值,而忽略之前的定义。这可能会导致一些不一致和混乱。
  • 去掉了重复属性的最后一个:添加了一些代码,使得构建系统在遇到重复属性时,删除最后一个定义的属性,并保留第一个定义的属性。这样,当你执行make时,构建系统会删除custom.mk中的强制赋值,并将这个属性设置为device.mk中的强制赋值(240)。例如:
rk_android12.0_sdk/out/target/product/rk3568_s/vendor$ grep -rn "ro.sf.lcd_density"
  #build.prop:92:ro.sf.lcd_density=240
  # Removed by post_process_props.py because Duplicate property, keeping the first one
  build.prop:161:#ro.sf.lcd_density=320

这样做的问题是,不想让device.mk中的赋值覆盖custom.mk中的赋值,而是反过来,但结果却是注释掉了custom.mk的240。

按照正常理解 应该是custom.mk(240) 比device.mk(320) 后加载应该删除320才对?但是py脚本加载写入是倒序的?这不符合我的预期。

  • 去掉了重复属性的第一个:继续修改,使得构建系统在遇到重复属性时,删除第一个定义的属性,并保留最后一个定义的属性。这样,当你执行make时,构建系统会删除device.mk中的强制赋值,并将这个属性设置为custom.mk中的强制赋值(320)。例如:
# Removed by post_process_props.py because Duplicate property, keeping the last one
build.prop:92:#ro.sf.lcd_density=240
# Removed by post_process_props.py because Duplicate property, keeping the first one
#build.prop:161:ro.sf.lcd_density=320

这样做可以让custom.mk中的赋值覆盖device.mk中的赋值,而不用修改原生的device.mk文件中的代码 不管系统之前属性如何写的,只需要最终在我们的客制化mk文件中重新覆盖即可,不需要整个源码的去找。但是这样做的缺点是,可能会影响其他属性的赋值顺序和结果,因为改变了构建工具的默认行为。但根据我的测试暂时没什么影响。

总结

在本文中,介绍了Android源代码定制的一些机制和工具,包括:

  • mk文件:介绍了mk文件的结构和语法,以及如何确定mk文件的执行顺序。
  • 属性(sysprop):介绍了属性的类型和赋值方式,以及如何覆盖属性的值。

希望这篇文章能够对你有所帮助,如果你有任何问题或建议,请在评论区留言。谢谢!

相关文章
|
19天前
|
Android开发 开发者
Android自定义View之不得不知道的文件attrs.xml(自定义属性)
本文详细介绍了如何通过自定义 `attrs.xml` 文件实现 Android 自定义 View 的属性配置。以一个包含 TextView 和 ImageView 的 DemoView 为例,讲解了如何使用自定义属性动态改变文字内容和控制图片显示隐藏。同时,通过设置布尔值和点击事件,实现了图片状态的切换功能。代码中展示了如何在构造函数中解析自定义属性,并通过方法 `setSetting0n` 和 `setbackeguang` 实现功能逻辑的优化与封装。此示例帮助开发者更好地理解自定义 View 的开发流程与 attrs.xml 的实际应用。
Android自定义View之不得不知道的文件attrs.xml(自定义属性)
|
19天前
|
Java Android开发
Android studio中build.gradle文件简单介绍
本文解析了Android项目中build.gradle文件的作用,包括jcenter仓库配置、模块类型定义、包名设置及依赖管理,涵盖本地、库和远程依赖的区别。
174 19
|
19天前
|
存储 XML Java
Android 文件数据储存之内部储存 + 外部储存
简介:本文详细介绍了Android内部存储与外部存储的使用方法及核心原理。内部存储位于手机内存中,默认私有,适合存储SharedPreferences、SQLite数据库等重要数据,应用卸载后数据会被清除。外部存储包括公共文件和私有文件,支持SD卡或内部不可移除存储,需申请权限访问。文章通过代码示例展示了如何保存、读取、追加、删除文件以及将图片保存到系统相册的操作,帮助开发者理解存储机制并实现相关功能。
258 2
|
4月前
|
移动开发 安全 Java
Android历史版本与APK文件结构
通过以上内容,您可以全面了解Android的历史版本及其主要特性,同时掌握APK文件的结构和各部分的作用。这些知识对于理解Android应用的开发和发布过程非常重要,也有助于在实际开发中进行高效的应用管理和优化。希望这些内容对您的学习和工作有所帮助。
414 83
|
8月前
|
ARouter Android开发
Android不同module布局文件重名被覆盖
Android不同module布局文件重名被覆盖
|
8月前
|
ARouter Android开发
Android不同module布局文件重名被覆盖
Android不同module布局文件重名被覆盖
440 0
|
12天前
|
安全 Java Android开发
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
40 0
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
|
3月前
|
JavaScript Linux 网络安全
Termux安卓终端美化与开发实战:从下载到插件优化,小白也能玩转Linux
Termux是一款安卓平台上的开源终端模拟器,支持apt包管理、SSH连接及Python/Node.js/C++开发环境搭建,被誉为“手机上的Linux系统”。其特点包括零ROOT权限、跨平台开发和强大扩展性。本文详细介绍其安装准备、基础与高级环境配置、必备插件推荐、常见问题解决方法以及延伸学习资源,帮助用户充分利用Termux进行开发与学习。适用于Android 7+设备,原创内容转载请注明来源。
539 76
|
4月前
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
289 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
4月前
|
JavaScript 搜索推荐 Android开发
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
115 8
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡