During Android development we often have to manage many different resources. At compilation time these resources are all gathered up and merged together into a single package.
As per the ‘The build process’ in the Configure your build documentation
The compilers convert your source code into DEX (Dalvik Executable) files, which include the bytecode that runs on Android devices, and everything else into compiled resources.
The APK Packager combines the DEX files and compiled resources into a single APK…
But what happens if there is a resource naming collision? Turns out that “it depends”, especially when libraries are involved. In this article we’ll explore some different resource collision cases and look at what it means to have good resource naming hygiene.
In the App Module
In the simplest case we have a collision between two resources with the same name and type in the same file, like in the following example:
Attempting to compile this results in a very self explanatory error message:
Similarly if we were to define these resources over multiple files:
We get a similar compilation error, however this time listing both files involved in the conflict:
The way resources work in Android starts to become apparent. In our app module we need to be able to resolve a unique resource for the combination of type, name and device configuration. That is, there needs to be only one resolvable value for string/hello_world when referenced from the app module. We (the developer) are expected to resolve this conflict by either removing the resource (if it is a duplicate), renaming one of the instances or by moving one instance to a resource file with an appropriate qualifier. More information on resources and qualifiers can be found in the App resources overview documentation.
Library and App Module
The next case we’ll investigate is when we have a resource defined in a library module and the duplicate defined in our app module:
Attempting to compile this… succeeds! From our previous discoveries we can now infer that Android must have a rule for resolving a single value for string/hello when encountering this scenario.
As per the Create an Android library documentation:
The build tools merge resources from a library module with those of a dependent app module. If a given resource ID is defined in both modules, the resource from the app is used.
However what implications does this have when building a modularised application? Say we define the following view in our library:
Looking at the preview for this view shows us:
And now when we decide to include the text view layout in a layout inside the app module:
When looking at the preview / the running application we can see that the text displays as:
Not only does accessing string/hello from a layout in the app module resolve to “Hello from the App!” but the app module’s version is also used for layouts included from libraries! For this reason we need to be wary of overriding resources defined in libraries when we don’t mean to.
Two libraries
In the final case we’ll look at what happens when conflicting resources are defined in two libraries. Following on from our previous example, if we have a setup like:
What will be rendered for the value string/hello? Turns out it depends on the ordering of dependencies in our app’s build.gradle.
Again from the Create an Android library documentation:
If conflicts occur between multiple AAR libraries, then the resource from the library listed first in the dependencies list (toward the top of the dependencies block) is used.
This means for the configuration:
The value of string/hello will be Hello from Library 1!. Subsequently if the two implementation lines are reordered such that implementation project(“:library2”) comes before implementation project(“:library1”) then the value will be Hello from Library 2!. This is quite a subtle effect and it’s pretty easy to see how it could result in unintended behaviour for our application.
Custom Attributes
So far all of our examples have been making use of string resources, however an interesting resource type to take particular notice of is custom attributes.
Consider the following definition:
One may think that the following would compile without issue, however when defined in the app module we get the following compilation error:
And if each custom attribute is defined across two libraries like so:
It… compiles. However if we change library2’s definition to be a different format (e.g. <attr name=”freeText” format=”boolean”/>) we get the much more obtuse compilation error:
One important part of the message to look into is:
mergeDebugResources/merged.dir/values/values.xml: AAPT: error: file failed to compile.
What’s going on here? The compilation of values.xml actually refers to the generation of the R class for the app module. During this compilation AAPT is trying to generate a single value for each of the properties for our R class. For each custom attribute inside a styleable there are actually two R class values which are generated. The first is the styleable name spaced value (under R.styleable), the second is the global value (under R.attr). For our particular case we are getting a collision on the global value, and due to the naming collision there are only three values to resolve as detailed below:
- R.styleable.CustomStyleable_freeText which is the value from library1 resolves to the attribute freeText with a format of string
- R.styleable.CustomStyleable2_freeText which is the value from library2 resolved to the attribute freeText with a format of boolean
- R.attr.freeText this value cannot be resolved as we have included values from both library1 and library2 and their format’s differ, this causes a collision.
In the first case where the format was the same across libraries R.attr.freeText was resolved to a single definition for the app module.
It’s worth noting at this time that each module has its own R class generated. We can’t always expect the value to remain consistent across our modules. Again from the Create an Android library documentation:
When you build the dependent app modules, library modules are compiled into an AAR file then added to the app module. Therefore, each library has its own R class, named according to the library’s package name. The R class generated from main module and the library module is created in all the packages that are needed including the main module’s package and the libraries’ packages.
Conclusion
So what do we take away from all of this? That resource compilation is complex and nuanced? Well actually, yes. However there are things that we can do as a developer to make what is going on obvious to ourselves and our team, namely resource prefixing. From our favourite Create an Android library documentation there is this hidden gem:
To avoid resource conflicts for common resource IDs, consider using a prefix or other consistent naming scheme that is unique to the module (or is unique across all project modules).
Taking on this advice, it would be a good idea to establish a pattern in our projects and teams where we prefix all resources in modules with the module name e.g. library_help_text. This has two side effects:
- It decreases the chance of collision considerably.
- It makes overrides explicit. E.g. creating library_help_text in the app module is now clearly overriding something in the library module. Sometimes we do want to perform these overrides and using prefixes helps to make it clear what is occurring during the build process.
At a minimum all public resources should be prefixed, especially when distributing our library as a vendor or as an open source project. From experience not all of the resources in Google’s own library’s are prefixed appropriately. This can have the unfortunate side effect of the inclusion of our library causing an app compilation to fail due to a naming collision. Not a great look!
For instance we can see the material design library prefixes their color resources consistently with mtrl. However the attribute resources nested under a styleable are not prefixed with material. As we have seen, this has the potential to conflict when generating the R class for a module which includes both the material library and a library containing an attribute of the same name but different format.
Acknowledgments
The creation of this blog post was assisted by the following content: