前情提要:
[实战笔记]系统编译——新增客制化资源包_local_aapt_flags-CSDN博客
解决了新增资源包管理的问题。新的问题来了,如果需要用作overlay的文件,希望替换原有包里的资源,怎么解决?
1.直接使用link方案
直接添加新资源包,会发现编译报错。错误大概意思为:编译中发现有重名的资源。
2.为什么要用文件去替换原有包里的资源?而不是直接修改原有资源?
对于ROM定制来说,framework资源和自用资源是需要分离的,一个是降低对framework的修改,另一个是便于移植。
为了解决这种问题,Google官方其实已经提供了overlay的框架。使用这个框架之前,还是需要科普一下资源打包构建的基础知识。
aapt2
aapt2是Android提供的一种构建工具,会解析资源、为资源编制索引,并将资源编译为针对 Android 平台进行过优化的二进制格式。官方文档如下:
AAPT2 | Android 开发者 | Android Developers
简单来说,aapt2的执行分为编译和链接两个步骤。
编译
aapt2接受一个资源文件,并解析该文件生成一个扩展名为 .flat
的中间二进制文件
尝试使用aapt2进行编译:
aapt2路径:
android_sdk/build_tools/(sdk_version)/aapt2.exe
cmd或者使用powershell到这个路径下就可以执行aapt2命令了。
aapt2编译语法:
aapt2 compile path-to-input-files [options] -o output-directory/
一般在工程中的语法为:
aapt2 compile project_root/module_root/src/main/res/values-en/ strings.xml -o compiled/
在aapt2.exe的路径下放置名为ic_volume.png的资源,创建输出目录 compiled,简单尝试下编译:
aapt2 compile ic_volume.png -o compiled\
报错:
error: invalid file path 'ic_volume.png'
原因是所有的资源文件编译成中间二进制文件时都会编译成(resource-type)_name.flat,resource-type对应的是资源文件放置的目录path,即drawable/values/layout等,因此资源文件需要放在某个类型的目录中。
修改:
在aapt2.exe的路径下创建drawable目录,其中放置名为ic_volume.png的资源,创建输出目录 compiled,简单尝试下编译:
aapt2 compile drawable\ic_volume.png -o compiled\
在compiled目录中输出:drawable_ic_volume.png.flat
链接
aapt2 link -o output.apk
-I android_sdk/platforms/android_version/android.jar
compiled/res/values_values.arsc.flat
compiled/res/drawable_Image.flat --manifest /path/to/AndroidManifest.xml -v
链接的过程会打包出apk,因此需要编译工具android.jar, apk中包含的AndroidManifest.xml,需要的flat文件。
创建目录 input, 将需要的资源都放在这个目录下,如某个工程中的AndroidManifest.xml,编译需要的资源目录drawable/ic_volume.png,然后执行命令试试看:
aapt2 link -o .\compiled\newApk.apk -I ..\..\platforms\android-33\android.jar .\compiled\drawable_ic_volume.png.flat --manifest .\input\AndroidManifest.xml -v
note: including ..\..\platforms\android-33\android.jar.
aapt2.exe W 10-08 12:04:54 13732 3156 LoadedArsc.cpp:657] Unknown chunk type '200'.
note: linking package 'com.tahlia.hmrefactormvvm' using package ID 7f.
note: merging 'drawable/ic_volume' from compiled file .\input\drawable\ic_volume.png.
note: enabling pre-O feature split ID rewriting.
.\input\AndroidManifest.xml:5: error: resource mipmap/ic_launcher (aka com.tahlia.hmrefactormvvm:mipmap/ic_launcher) not found.
.\input\AndroidManifest.xml:5: error: resource string/app_name (aka com.tahlia.hmrefactormvvm:string/app_name) not found.
.\input\AndroidManifest.xml:5: error: resource mipmap/ic_launcher_round (aka com.tahlia.hmrefactormvvm:mipmap/ic_launcher_round) not found.
.\input\AndroidManifest.xml:5: error: resource style/DefaultAppTheme (aka com.tahlia.hmrefactormvvm:style/DefaultAppTheme) not found.
error: failed processing manifest.
其中newApk.apk是希望生成的apk,android.jar是用于提供android命名空间的属性的包,drawable_ic_volume.png.flat是编译生成的中间二进制文件。
发现compiled目录下出现了newApk,但是里面是空的。查看上面打印,发现是因为一些资源找不到(因为没有给,只单独复制了某个工程的AndroidManifest.xml)。
于是将AndroidManifest.xml中说找不到的统统删除,重新试一次:
aapt2 link -o .\compiled\newApk.apk -I ..\..\platforms\android-33\android.jar .\compiled\drawable_ic_volume.png.flat --manifest .\input\AndroidManifest.xml -v
note: including ..\..\platforms\android-33\android.jar.
aapt2.exe W 10-08 12:06:46 13256 4516 LoadedArsc.cpp:657] Unknown chunk type '200'.
note: linking package 'com.tahlia.hmrefactormvvm' using package ID 7f.
note: merging 'drawable/ic_volume' from compiled file .\input\drawable\ic_volume.png.
note: enabling pre-O feature split ID rewriting.
AndroidManifest.xml: note: writing to archive (keep_raw_values=false).
note: writing AndroidManifest.xml to archive.
note: writing res/drawable/kk_ic_volume.png to archive.
note: writing resources.arsc to archive.
查看compiled目录下,已经生成newApk.apk,放入apkanalyzer,能看到正确内容:
打包成功。
添加overlay资源包
overlay的优先级
Google针对overlay是声明了两种等级:
DEVICE_PACKAGE_OVERLAYS
PRODUCT_PACKAGE_OVERLAYS
从名字和AOSP的产品定义架构,不难看出,这两种定义的优先级为: DEVICE_PACKAGE_OVERLAYS > PRODUCT_PACKAGE_OVERLAYS。
而每一种overlay又可以接受多个路径,路径在前的优先级越高。例如下图:
即可得出overlay资源包的优先级为:
[DEVICE] device/xxxx/overlay > [DEVICE] vendor/xxxx/overlay/static > [DEVICE] device/xxxxx/overlay_res > [PRODUCT] (空)
overlay的声明定义
在AOSP中用上资源包,需要在mk/bp中声明添加。
在mk中添加:
# overlay
# DEVICE_PACKAGE_OVERLAYS += resPath
# PRODUCT_PACKAGE_OVERLAYS += resPath
DEVICE_PACKAGE_OVERLAYS += $(LOCAL_XXXX_PATH)/XXXX_res
bp暂时还没用到,使用androidmk转换一下就能出结果了。
overlay资源包添加遇到的问题
1.DEVICE_PACKAGE_OVERLAYS变量不可修改
在mk中定义了之后,编译出现如下问题:
FAILED:
Android.mk:4: error: cannot assign to readonly variable: DEVICE_PACKAGE_OVERLAYS
15:33:14 ckati failed with: exit status 1
这个是因为 DEVICE_PACKAGE_OVERLAYS 需要配置好环境后才可以编辑,所以报错。也就是说这个mk调用的时间不合适,需要时机后移。
解决办法:可以放在其他mk中。针对这个情况我是放在了device下使用的mk。
2.multiple default products defined for resource
查看错误:
frameworks/base/core/res/res/values/strings.xml:3218: error: multiple default products defined for resource android:string/android_upgrading_title.
device/xxxx/overlay/frameworks/base/core/res/res/values/strings.xml:22: note: default product also defined here.
frameworks/base/core/res/res/values/strings.xml:3218: error: multiple default products defined for resource android:string/android_upgrading_title.
device/xxxx/overlay/frameworks/base/core/res/res/values/strings.xml:22: note: default product also defined here.
frameworks/base/core/res/res/values/strings.xml:3218: error: multiple default products defined for resource android:string/android_upgrading_title.
device/xxxx/overlay/frameworks/base/core/res/res/values/strings.xml:22: note: default product also defined here.
frameworks/base/core/res/res/values-zh-rCN/strings.xml:1191: error: multiple default products defined for resource android:string/android_upgrading_title.
device/xxxx/overlay/frameworks/base/core/res/res/values-zh-rCN/strings.xml:23: note: default product also defined here.
frameworks/base/core/res/res/values-zh-rTW/strings.xml:1191: error: multiple default products defined for resource android:string/android_upgrading_title.
device/txxxx/overlay/frameworks/base/core/res/res/values-zh-rTW/strings.xml:23: note: default product also defined here.
可以简单理解为重复定义。overlay实际上是对原有包中的资源进行覆盖的,因此是直接替换的原有资源,而无需定义。理论上overlay和framework不是一个等级的,继续排查。查看脚本:
out/soong/host/linux-x86/bin/aapt2 link
-o out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/package-res.apk
--private-symbols com.android.internal
--no-auto-version
--auto-add-overlay
--no-static-lib-packages
--manifest out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/manifest_fixer/AndroidManifest.xml
-A frameworks/base/core/res/assets
--min-sdk-version 29
--target-sdk-version 29
--version-code 29
--version-name 10
--product tv
-c zh_CN,normal,large,xlarge,hdpi,xhdpi
--preferred-density xhdpi
--java out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/aapt2/R
--proguard out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/proguard.options
--output-text-symbols out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/R.txt @out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/aapt2/res.list
-R @out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/aapt2/overlay.list && out/soong/host/linux-x86/bin/soong_zip -write_if_changed -jar -o out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/R.jar
-C out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/aapt2/R
-D out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/aapt2/R &&out/soong/host/linux-x86/bin/extract_jar_packages
-i out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/R.jar
-o out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/extra_packages
--prefix '--extra-packages'
找到-R的那部分是关于overlay的,查看overlay-list,发现里面也有google的overlay 不同语言string的记录,所以和string这个资源类别也没关系。
继续对比这个资源在AOSP中的定义和overlay中的定义,发现这个平台版本的资源是区分了product的,而我们在overlay中的并没有区分prodcut:
# AOSP中
<string name="android_upgrading_title" product="default"> xxxx </string>
# overlay中
<string name="android_upgrading_title"> xxxx </string>
解决方法:
资源声明是包括命名、产品几个区分维度的。ovrelay资源需要完全匹配资源的声明,否则就会报资源重复定义的错误。