How does the Android Gradle plugin handle conflicting resources in libraries? - android

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

Related

How can I have a different package name for debug and release in a library module?

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

Resources merging priority on android gradle builds

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.

How to force android project to merge the resources from feature module?

I have a multi-module project with base-feature and feature modules.
The problem is that after the build the resources from feature module are duplicated with a second id and the resources are not merged properly in the end. Is there a something about the gradle plugin which should be done to make this run? ==> com.android.feature
When I run the project it tries to find the resource from feature module with the feature module resource id, but in the end the main module has the same resource already with another id, which I guess should be merged.
This is an intended behavior. Please make sure that your resource IDs are unique. Refer to this Android Instant Apps FAQ: “Can I share resources between features?”
https://developer.android.com/topic/instant-apps/faqs.html#project-structure
However, you must keep the IDs for your resources unique between your
dependent features and base feature. For example, if your base feature
provides a resource with the ID R.id.feature_layout and a dependent
feature defines another resource with the same ID, the instant app
uses the resource from the base feature instead of the dependent
feature.

best practice for flavored build variants with gradle in android studio

I have an App that is produced for different brands. Now I have two flavors, brand1 and brand2. The difference between brand1 and brand2 are only the resource files.
First I assumed I could set all shared data into my main path and the differences into the flavored path. But in this scenario my build failed, because resources in my main path are missing.
I would go on like this: set all resource differences between the brands in the main path as dummy data. The merge will do the rest for me.
In iOS I can set a build path for each build target.
Is something like this possible with flavors? Or what is the best practice in this situation?
[EDIT 2013 08 07]
The problem in my case was that I created the app in eclipse and exported it to android studio.
Here the problem and solution is described more precisely.
Another solution is to put everything shared (basically the a "vanilla" version of your app) as a libary project, including default resources.
Then you can create seperate "brand" projects that use the library project. Any resources you put in these will "overwrite" the library project resources.
http://developer.android.com/tools/projects/projects-eclipse.html#SettingUpLibraryProject
I've done this for clients and it's worked quite well.

Why Android cannot deal with Resources name conflict between Project and Library?

I have a project A that referenced by Library B, A and B have the same name and type, but their value are different. I think aapt should deal with this issue that make sure project and library access the correct value. besides renaming all the resource in project or library, what else should I do to solve this problem?
The build system intentionally makes all project resources overlay on top of the library resources. This is done on purpose to be able to customize a library resource differently depending on the app using it.
If you want to prevent this happening without your knowledge we have always recommended users to use prefix in the library resources.
Changing the behavior at this point would break many, many people's projects. We've looked at making it an option, but it won't happen before the new build system is finished though.
As per the Android Building process, all projects and libraries (and all of the resources in all of them) are combined as part of the apkbuilder process. If there is a conflict between your project and library (or between two libraries), the final build will not know which to reference as they share the same name. Of course, this has benefits in that you can reference library resources in your project by name, even though the underlying build process is de-conflicting the underlying ids.
Import the appropriate R.java file to resolve the resource conflicts.
Make sure that your package name and the library's package names are different.
If you need your libraries resources then refer to them by library.packagename.R.drawable.resourceId and not by R.drawable.resourceId
Check your gen files if it has 2 R.java files.

Categories

Resources