Flo*_*ern 6 android android-manifest android-studio android-gradle-plugin android-studio-2.0
我使用以下Gradle脚本在编译时对AndroidManifest.xml进行一些修改.在这个例子中,我想注入一个<meta-data>元素.代码基于这个答案.
android {
// ...
applicationVariants.all { variant ->
variant.outputs.each { output ->
output.processManifest.doLast {
def manifestOutFile = output.processManifest.manifestOutputFile
def newFileContents = manifestOutFile.getText('UTF-8').replace("</application>", "<meta-data ... /></application>")
manifestOutFile.write(newFileContents, 'UTF-8')
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
当我在Android Studio中执行Gradle同步或从命令行进行干净构建时,这可以正常工作:可以从应用程序中访问元数据.
但是当我从Android Studio运行▶应用程序时,修改后的清单似乎被忽略了,因为插入的元数据不是APK中编译清单的一部分,而且应用程序本身也无法在运行时找到它,元数据根本就不存在.
在所有的情况下,合并后的中间AndroidManifest.xml中(在/构建/中间体/舱单/)不包含的变化,但由于某种原因它看起来像它,如果我运行的应用程序被忽略.
为了使它更加明显,我尝试插入一些无效的XML:在这种情况下,由于清单中的语法错误,Gradle同步和干净构建按预期失败.但我仍然可以从Android Studio运行该应用程序,因此有效地修改了修改.
重现这一点的最简单方法是首先清理项目(在Android Studio中),这会导致清单被重新处理(如果出现语法错误,我会按预期失败),然后运行应用程序,即使使用无效的清单.
请注意,doLast每次都会执行println()任务:打印任务中的A ,中间清单包含更改.
就好像在我的任务执行之前将清单编译到APK中一样.
这里的问题在哪里?
我正在使用Android Studio 2.0和Android Gradle Plugin 2.0.0.
Flo*_*ern 15
我发现它与Android Studio 2.0中引入的Instant Run功能有关.如果我将其关闭,一切都按预期工作.但是因为我想使用Instant Run,所以我进一步挖掘了一下.
问题是,启用Instant Run后,中间AndroidManifest.xml文件将位于另一个位置,即/ build/intermediates/bundles/myflavor/instant-run /.这意味着我正在有效地编辑错误的文件.可以使用属性访问其他清单文件,可以使用该文件instantRunManifestOutputFile代替manifestOutputFile.
为了使它在所有用例中都能工作,我检查它们是否存在的临时清单文件并修改它们,如果是这样的话:
applicationVariants.all { variant ->
variant.outputs.each { output ->
output.processManifest.doLast {
[output.processManifest.manifestOutputFile,
output.processManifest.instantRunManifestOutputFile
].forEach({ File manifestOutFile ->
if (manifestOutFile.exists()) {
def newFileContents = manifestOutFile.getText('UTF-8').replace("</application>", "<meta-data ... /></application>")
manifestOutFile.write(newFileContents, 'UTF-8')
}
})
}
}
}
Run Code Online (Sandbox Code Playgroud)
实际上没有文档instantRunManifestOutputFile.我获得的唯一Google搜索结果是Android Gradle Plugin源代码.但后来我还发现了第三个潜在的清单文件属性aaptFriendlyManifestOutputFile,我不知道它是什么...
I want to add some additional information to this question. The answer from @Floern is a bit outdated. The code is working on old Gradle versions. The new version of Gradle says that manifestOutputFile is deprecated and will be removed soon. instantRunManifestOutputFile doesn't exists at all. So, here is the example for the new Gradle version:
applicationVariants.all { variant ->
variant.outputs.each { output ->
output.processManifest.doLast {
def outputDirectory = output.processManifest.manifestOutputDirectory
File manifestOutFile = file(new File(outputDirectory, 'AndroidManifest.xml'))
if(manifestOutFile.exists()){
// DO WHATEVER YOU WANT WITH MANIFEST FILE.
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
EDIT: Here is the newer variant for Gradle 5.4.1 and Grudle plugin 3.5.1:
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
def processManifest = output.getProcessManifestProvider().get()
processManifest.doLast { task ->
def outputDir = task.getManifestOutputDirectory()
File outputDirectory
if (outputDir instanceof File) {
outputDirectory = outputDir
} else {
outputDirectory = outputDir.get().asFile
}
File manifestOutFile = file("$outputDirectory/AndroidManifest.xml")
if (manifestOutFile.exists() && manifestOutFile.canRead() && manifestOutFile.canWrite()) {
// DO WHATEVER YOU WANT WITH MANIFEST FILE.
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
UPDATE:
For Gradle 7.0.2 and Gradle Plugin 7.0.2
instead of task.getManifestOutputDirectory() use task.getMultiApkManifestOutputDirectory()
Hope this will help someone.
| 归档时间: |
|
| 查看次数: |
2372 次 |
| 最近记录: |