We want to split our code base into several android library modules. One of them contains common-shared resources (strings, drawables, etc).
The problem arises when one of those resources, let's say app_name, is also defined in another external library. In that case, when all modules are merged, the resource with the id app_name is selected from an external library (randomly?), discarding the resource defined in our own local library module.
Is there a way to set a priority when merging resources to favour an specific library, or at least a module defined locally?
I faced off with this problem in the past and this is how I resolved it:
In your app module (your main module) adds this to the build.gradle file:
sourceSets {
main {
res.srcDirs =
[
'../my-module/src/main/res',
'src/main/res'
]
}
}
../my-module/src/main/res -> In this way your are referencing to the resources stored in the relative path to your library module (my-module)
src/main/res -> Needed to reference to other resources that you have in your app module
Android doc:
Change your resource directory -> https://developer.android.com/studio/write/add-resources.html#change_your_resource_directory
Resource merging -> https://developer.android.com/studio/write/add-resources.html#resource_merging
Update 2022;
Local libraries should already have priority over third-party ones,
but to prioritize a local-library over another local-library, simply change your include order.
Open your settings.gradle file, and ensure to include by order of what you want to have priority over others, like:
include ':app' // Heighest priority.
include ':my-library'
include ':my-other-lib' // Lowest priority.
Related
I created a custom Lint check and want the check applied to all dependent modules in my Android project. The custom Lint check lives in the checks module, and I can successfully run it in other modules by adding lintChecks project(":checks") to their respective build.gradle files. What I'd like to do is add something similar to api <dependency>, so all dependent modules also run the check. There is a core module already serving the purpose for other functionalities. Is there an API or configuration to prevent lintChecks redundancy? Such that all I'd have to do is add lintChecks project(":checks") to the core module.
I did not create an IssueRegistry file in the correct directory, resulting in Gradle not showing the task. Found out lintChecks in build.gradle files was never necessary.
Short question:
How can I change the package name for debug or release build type in a library module?
Context:
In an Android project with MVP + Clean architecture, we have the repository pattern in a library module. We want to include Firebase on that library with two environments (development and production).
I already created the project in Firebase (com.example.com and com.example.com.dev) and then downloaded the respective google-services.json to src/main and src/debug folders.
Gradle google-services plugin validates the module package name with the client ID defined in the google-services.json, but Android restrict to change the applicationId in a library (I can't find the technical reason why)
Things that I tried:
Have two AndroidManifest.xml with different package property. com.example.com in the src/main and com.example.com.dev in src/debug/ but the second one it is just ignored
Set manifest.srcFile in Gradle sourceSets. The file is in the list when I run ./gradlew sourceSets but the package name doesn't change
Two different flavors in the library module and set different manifests for each one. The package anyway doesn't change.
At this moment I have just two suitable solutions:
1. Keep the Firebase setup and implementation in the app module outside of the repository.
2. Have only one environment for Firebase.
Thanks a lot for any help or advice.
EDIT:
Consider that I need to modify the package in a module (library), not in the app. For some weird reason Gradle shows this error when I try to use applicationIdSuffix or applicationId in a module:
ERROR: Library projects cannot set applicationIdSuffix.
Library projects cannot set applicationId.
This is the designed behaviour of Android Library project.
Two different flavors in the library module and set different
manifests for each one. The package anyway doesn't change.
Probably you need to change your Build Variants to that particular flavor/buildtype then you can see the updated package name taking effect.
See below screenshot.
I spent some time investigating and trying different alternatives to achieve two different package name per flavor or build type in a library module, and my short answer is: You can't, at least without doing something tricky and dirty.
Some guys that recommend using the parameter applicationIdSuffix or applicationId, that option doesn't work because of the design of library modules restricts those parameters to the app module. In a library, the package must be defined in the manifest.
That restriction delimits options to flavors, but, the package attribute in the manifest isn't merged, and you get an error when having different package name for the scr/main/AndroidManifest.xml and src/flavor/AndroidManifest.xml.
A tricky solution is to get the currently selected flavor form the task name and then change the main manifest in the sourceSets.
sourceSets {
main {
manifest.srcFile "src/$flavor_name/AndroidManifest.xml"
}
}
This hack works but in general is a bad idea, and I prefer to change the design of my architecture and implement the repository for both Firebase variants in a different way.
Gradle plugin: 3.2.0
Good day. I've written a kotlin android library and uploaded it on bintray. But when I try to use it (via gradle compile) in some project, it fails to build with following errors:
> com.android.build.api.transform.TransformException: com.android.builder.packaging.DuplicateFileException: Duplicate files copied in APK META-INF/library_release.kotlin_module
File1: C:\Users\Charlie\.android\build-cache\2939fbc6b0336396c9c4912d615880645b2c729d\output\jars\classes.jar
File2: C:\Users\Charlie\OneDrive\Dev\Projects\AndroidStudio\MetuCardLib\demo\build\intermediates\bundles\default\classes.jar
I've looked up both of these .jar files and they both contained META-INF folder with library_release.kotlin_module file. But more importantly generated .aar (android archive published in bintray) contained this folder as well as this file. I've checked other decent bintray android libraries and they don't seem to include any META-INF files. However those that do include it (in most cases they contain license files) produce the same DuplicateFileException and the way to resolve it is to explicitly exclude them in use-project's gradle file.
What's the use of this library_release.kotlin_module file and how can I disable its generation during publishing? Because I don't want to explicitly exclude from every project which is using this library and I don't want to ask other developers to do so.
Here's the library's repo: https://github.com/arslancharyev31/Anko-ExpandableTextView/
And it's bintray repo: https://bintray.com/arslancharyev31/android/Anko-ExpandableTextView
The original purpose of .kotlin_module files is to store the package parts mapping to the compiled class files. The compiler uses them to resolve top-level function calls in a library attached to the project. Also, Kotlin reflection uses the .kotlin_module files to construct the top level members metadata at runtime. See more: Improving Java Interop: Top-Level Functions and Properties
Given that, you don't want to disable their generation in library projects, because it might break compilation against the libraries.
Instead, you can get rid of the duplicates by using packagingOptions in your app build.gradle, as said here:
android {
// ...
packagingOptions {
exclude 'META-INF/library_release.kotlin_module'
}
}
Note: excluding these files from an application APK interferes with reflection at runtime and therefore is is not the best solution either.
Another option is to use unique module names in your libraries:
compileReleaseKotlin.kotlinOptions.freeCompilerArgs += ["-module-name", "my.library.id"]
Choose the task that produces the output that is then packed into the AAR.
It might not be compileReleaseKotlin, and also note that doing this might affect the tests compilation for this variant.
Or, what's more reliable, just choose unique Gradle module names, because the Kotlin module is named after the Gradle project.
I was able to resolve this by renaming modules to be uniquem when simpler module names conflicted.
EDIT: as we just learned the hard way - kotlin module name should only contain all filesystems supported characters.
We used ':' in the module name causing jar to contain file "company:product.kotlin_module". This will fail to extract on windows.
I thin in android realm currently(tested on Android gradle plugin 7.0.2)best solution would be to use android kotlin options section
android{
kotlinOptions{
moduleName = "com.example.mylibrary"
}
}
It results in META-INF/com.example.mylibrary.kotlin_module - which should be sufficently unique
I have an Android application project that depends on two Android libraries. The two Android libraries specify two resources with the same resource ID. In the old Ant build system, the priority of libraries was specified in a project.properties file, but such a file is not used in the Gradle build system.
Although the Resource Merging doc explains the priorities and merge process for resources that conflict between build types, product flavors, application projects, and library projects, it does not explain what occurs when two libraries (who have no common dependencies) are merged in a single project. During the merge process of the build process, how does the Android Gradle plugin determine which library's resource has a higher priority?
There is not a way to specify a priority for the library's resources.
You can only setup the prefix in your library with
android {
resourcePrefix 'mylib_'
}
Ah, looks like the Android developers documentation finally has an answer for us. I pulled this from https://developer.android.com/studio/projects/android-library#Considerations:
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.
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.
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).
I bolded the relevant paragraph. It looks like that the order in which dependencies can appear in a Gradle dependencies block can affect the build process! This is a small thing to be wary of.
#Angela answer is great for solving your (and actually my) problem, for answering your problem according to how Android choose the right resource and what exactly the priority:
From the resource merging part in the build process:
The priority order is the following:
BuildType -> Flavor -> main -> Dependencies.
...
This means that if src/main/res has
res/layout/foo.xml
res/layout-land/foo.xml
and src/debug/res has
res/layout/foo.xml
Then the merged resource folder will contain the default foo.xml from
src/debug/res but the landscape version from src/main/res
I have one project with two modules: the app and the lib.
Obviously the app module depends of the lib module.
However I need that the lib module uses resources (image drawables) from app module. Is possible?
No, because all library modules are compiled first, and they do not have any access to the application resources at this point. A library module must include all of the resources it is dependent on by definition. Only when the application is compiled do its resources get built into the relevant R classes, at which point the library is already fully compiled.
No. The problem is that resource id-s are assigned dynamically, so you can't refer to a resource from a library, since its id is unpredictable.