Welcome to follow my public numberEfficient Android Development“Focuses on Android project efficiency and development experience, covering topics such as infrastructure, Kotlin Multiplatform, Gradle construction and optimization, etc. At the same time, we also talk about overseas work and life, and push the latest Podcast of” Two-part radio “.

“Build North” is a series of articles exploring Android building. It covers Gradle, Android Gradle Plugin, Kotlin Script, and other tools, as well as related architecture applications. To find the problem to solve the problem as the starting point, transfer new knowledge to improve production efficiency as the foothold.

Although this is a problem encountered earlier in this paper, the tool discussed is AAPT1, but its debugging methods, methods used and the resulting library can be applied to AAPT2, you can check github.com/2BAB/Seal warehouse for more up-to-date usage.)

In recent SDK updates, some packages were introduced with an error like this:

AndroidManifest.xml:22:9-40 Error:

Attribute application@theme value=(@style/AppTheme) from AndroidManifest.xml:22:9-40
is also present at [some:libraries:version] AndroidManifest.xml:9:18-62 value=(@style/AnotherTheme).
Suggestion: add 'tools:replace="android:theme"' to <application> element at AndroidManifest.xml:18:5-65:19 to override.
Copy the code

This is a very common mistake, just follow the instructions to replace. But when I add the replace code, I still get an error:

Multiple entries with same key: @android:theme=REPLACE and android:theme=REPLACE.

It also sets tools:replace=” Android :theme”, and the Manifest Merger throws this out as a conflict.

thinking

If you just follow the official Manifest Merge, this problem may not be solved. Someone on StackOverflow has asked this question, but no more answers.

Why would a dependent library not want to set the replace property? A big possibility is that he also encountered a conflict between his dependency library and his Manifest. So what can we do? We still want to replace some of his properties (theme/allowBackup/…). Whatever his intentions were, it didn’t stop me from wanting to hit the bag!

solution

Through simple observation and source code review, we find that merge occurs in the Process ${variant}Manifest Task. Find a way to Precheck all dependent Androidmanifest.xml before performing this task, and then write a script or plugin to clean up unwanted content.

Seal – A gradle plugin to do precheck of Android Manifest.

I wrote a simple plugin to do this and currently supports several features:

seal {

    // 0. Two examples of operations performed before the manifest merge
    beforeMerge("Remove description attr for library input Manifest.")
        .tag("application")
        .attr("android:description")
        .deleteAttr()
    beforeMerge("Remove problematic replace attr for library input Manifest.")
        .tag("application")
        .attr("tools:replace")
        .deleteAttr()

    // A complete example of the merged MANIFEST operation (1-5).
    // 1. This operation is dangerous. Specify as many detailed attributes and values as possible
    afterMerge("Remove all uses-feature tags.")
        .tag("uses-feature")
        .deleteTag()

    // 2. This operation is dangerous. Specify as detailed a value as possible
    afterMerge("Remove all custom permission tags.")
        .tag("permission")
        .attr("android:protectionLevel")
        .deleteTag()

    // 3. This is how we recommend removing the tag
    afterMerge("Remove invalid service tag.")
        .tag("service")
        .attr("android:name")
        .value("me.xx2bab.seal.sample.library.LegacyService")
        .deleteTag()

    // You should use "tools:remove" or "tools:replace" whenever possible to replace" deleteAttr"
    // 4. Delete the attribute and its value
    afterMerge("Remove application's allowBackup attr.")
        .tag("application")
        .attr("android:allowBackup")
        .deleteAttr()

    // You should use "tools:remove" or "tools:replace" whenever possible to replace" deleteAttr"
    // 5. You can also specify values as part of the query parameters
// afterMerge("Remove application's allowBackup attr.")
// .tag("application")
// .attr("android:allowBackup")
// .value("true")
// .deleteAttr()

}
Copy the code

The overall configuration is divided into three parts:

  1. choosebeforeMerge(ruleName: String)orafterMerge(ruleName: String)As the entrance to the hook
  2. By passing intag(name: String) attr(name: String) value(name: String)As a specified query parameter (regular expressions are not currently supported), be as precise as possible to ensure that the appropriate element is located
  3. choosedeleteTag()deleteAttr()To specify the type of deletion. Note that only one deletion method is executed. Do not call more than one deletion methoddeleteXXXmethods

For more configuration information, please refer to the instructions in Github warehouse. Welcome to mention PR and ISSUE.

Welcome to comment like, and follow my public accountEfficient Android Development.