I use the following Gradle script to make some modifications to the AndroidManifest.xml at compile time. In this example I want to inject a <meta-data>
element. The code is based on this answer.
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')
}
}
}
}
This works as expected when I do a Gradle sync in Android Studio or make a clean build from command line: The meta-data is accessible from within the app.
But when I run ▶ the application from Android Studio the modified manifest seems to be ignored, since the inserted meta-data is not part of the compiled manifest in the APK, and the app itself cannot find it at runtime either, the meta-data is simply not there.
In all cases the merged intermediate AndroidManifest.xml (in /build/intermediates/manifests/) does contain the changes, but for some reason it looks like it gets ignored if I run the app.
To make it even more obvious, I tried to insert some invalid XML: In this case, the Gradle sync and the clean build failed as expected because of a syntax error in the manifest. But I was still able to run the app from Android Studio, thus the modification effectively gets ignored..
The easiest way to reproduce this is to clean the project first (in Android Studio), which causes the manifest to be reprocessed (in case of the syntax error I get a failure as expected), and then run the app, which works even with an invalid manifest.
Note that the task in doLast
gets executed everytime: A println()
in the task is printed and the intermediate manifest contains the changes.
It's as if the manifest gets compiled into the APK before my task is executed.
Where is the issue here?
I'm using Android Studio 2.0 with the Android Gradle Plugin 2.0.0.
there are difference with various gradle version, for me, I Used
gradle-5.5-rc-3
andcom.android.tools.build:gradle:3.4.1
so this would work :the location of intermediates/merged_manifests was found from module's build directory, it could be others depends on android-plugin version, just look into the build directory and find yours.
I figured out that it's related to the Instant Run feature introduced in Android Studio 2.0. If I turn it off, everything works as expected. But since I want to use Instant Run, I digged a little further.
The thing is, with Instant Run enabled the intermediate AndroidManifest.xml file will be at another location, namely /build/intermediates/bundles/myflavor/instant-run/. That means I was effectively editing the wrong file. That other manifest file is accessible with the property
instantRunManifestOutputFile
, which can be used instead ofmanifestOutputFile
.To make it work in all use-cases I check both temporary manifest files whether they exist and modify them if so:
There is literally no documentation of
instantRunManifestOutputFile
. The only Google search result I got was the Android Gradle Plugin source code. But then I also found a third potential manifest file propertyaaptFriendlyManifestOutputFile
, which I don't know what it's about either...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:EDIT: Here is the newer variant for Gradle 5.4.1 and Grudle plugin 3.5.1:
Hope this will help someone.