This issue should solve my other issue where I need to update the child libraries content provider: Using build types in Gradle libraries to run same app that uses ContentProvider on one device
I have a product flavor in the root application that is successfully changing the package name so I can deploy different versions of the app. When I try to add the same product flavor in a child library, the build fails because the root application fails to load a java class that is referenced from the child library because now the package name has changed? I thought that product flavors did not effect the java class package structure?
ATCApp.gradle root application
...
dependencies {
...
compile project(':libraries:FYC')
...
}
...
android
{
...
productFlavors
{
prod {
packageName "com.company.android"
}
qa {
packageName "com.company.android.qa"
}
}
}
FYC.gradle child library
...
android
{
...
productFlavors
{
prod {
resValue "string", "authority", "com.company.android.fyc.models.listing.listingprovider"
}
qa {
resValue "string", "authority", "com.company.android.qa.fyc.models.listing.listingprovider"
}
}
}
Adding the above product flavors in the child FYC library causes the root application to throw an error:
/src/main/java/com/company/android/HomeBroadcastReceiver.java:7: package com.company.android.fyc.controllers does not exist
import com.company.android.fyc.controllers.FYCHomePagerActivity;
Thanks for any help!
I'm pretty sure you can't do dependencies on flavors (aka, chained flavors), as Gradle offers no way to express such a thing. Your root project can have different dependencies based on the flavor, but those dependencies cannot be explicit flavors themselves. The output of sub-projects should be predictable and consistent.
Related
I have created a library called mylibrary with two product flavors like virtual and nonvirtual like below
flavorDimensions "dev"
productFlavors {
virtual {
dimension "dev"
}
nonvirtual {
dimension "dev"
}
}
and I have implemented the library in my project LibraryTesting like below
implementation project(path: ':mylibrary')
Now, when I trying to select different build variant its showing errors like "Module 'LibraryTesting.mylibrary' has variant 'nonvirtualDebug' selected, but the module 'LibraryTesting.app' depends on variant 'virtualDebug'"
please find the below image for more information
I want change the library build variant and build the app with that selected variant.
Please help me.
Your app module must decide what flavor to use.
For example, if you want app module to compile myLibrary module and virtual flavor, you need to add the following to android.defaultConfig block in build.gradle of app module:
missingDimensionStrategy 'dev', 'virtual'
Alternatively, if you want to be able to choose the variants from UI without errors, you can create 2 flavors in app module and each flavor will compile the corresponding flavor in myLibrary module:
flavorDimensions "app_dev"
productFlavors {
virtual {
dimension "app_dev"
missingDimensionStrategy 'dev', 'virtual'
}
nonvirtual {
dimension "app_dev"
missingDimensionStrategy 'dev', 'nonvirtual'
}
}
In any case, app module must declare missing dimension strategy
My android app depends on multiple android library (say A,B,C,D). I would like to generate different apks out of them say apk1 needs to consume only on A,B and apk2 needs to consume B,C.
I've explored the option of Android's product flavor concept by doing something like this,
android {
...
defaultConfig {...}
buildTypes {
debug{...}
release{...}
}
// Specifies one flavor dimension.
flavorDimensions "version"
productFlavors {
demo {
// Assigns this product flavor to the "version" flavor dimension.
// If you are using only one dimension, this property is optional,
// and the plugin automatically assigns all the module's flavors to
// that dimension.
dimension "version"
applicationIdSuffix ".demo"
versionNameSuffix "-demo"
}
full {
dimension "version"
applicationIdSuffix ".full"
versionNameSuffix "-full"
}
}
}
Now it's generating apks with multiple products but it is including all libraries for each flavor. How can I avoid this?
Use flavor-specific directives for your dependencies. Instead of implementation, for example, you would use demoImplementation and fullImplementation. The demo builds would include the demoImplementation dependencies, while full builds would include the fullImplementation dependencies.
The documentation page that you linked to shows an example, using a free flavor:
dependencies {
// Adds the local "mylibrary" module as a dependency to the "free" flavor.
freeImplementation project(":mylibrary")
// Adds a remote binary dependency only for local tests.
testImplementation 'junit:junit:4.12'
// Adds a remote binary dependency only for the instrumented test APK.
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
I have an Android project that uses some native libs that are quite big in size.
I have several flavors in this project and some of them don't make use of these libs so I would like to exclude them from the APK.
I know several ways to achieve this but I would like to use some nice code in the build.gradle file to reduce possible errors and learn groovy.
I have to mention that there is a boolean buildConfigField (called DO_IT in this example). If DO_IT is false then JNI libs are to be excluded.
This is the way I do it now:
defaultConfig {
buildConfigField "boolean", "DO_IT", "true"
}
productFlavors {
flavor1 {
// for this flavor JNI libs will be included
}
flavor2 {
// for this flavor JNI libs will NOT be included
buildConfigField "boolean", "DO_IT", "false"
ndk {
abiFilters ''
}
}
}
Remarks:
1 - Consider that I have many flavors with tons of properties and I don't want to replicate the block
ndk {
abiFilters ''
}
but I cannot manage to put this block it inside a method.
2 - The pefect solution would just exclude the libs based on the DO_IT buildConfigField in a routine outside the flavors' blocks EG in the defaultConfig.
android-soexcluder should be the way to go.
Steps are as below:
Modify build.gradle to add the dependencies.
buildscript {
repositories {
//...
}
dependencies {
classpath 'com.jween.gradle:android-soexcluder:1.1'
}
}
apply plugin: 'com.android.application'
apply plugin: 'android-soexcluder'
Exclude the .so from your build flavor
soexcluder {
// Remove all so files according to the flavor1
flavor1 {
exclude "lib/armeabi-v7a/foo.so", "lib/arm64-v8a/bar.so"
}
}
Other option is to add a gradle parameter for packagingOptions: Set doNotStrip packagingOptions to a specific buildType
I use this pattern.
Project build.config
buildscript {
ext.DO_IT = true;
}
Module(app) build.config
productFlavors {
flavor2 {
externalNativeBuild {
cmake {
if (DO_IT) {
targets "someTarget"
}
}
}
}
}
NB.1 ext is mentioned in Extra properties section, an
ExtraPropertiesExtension of Gradle.
NB.2 Per flavor settings like including libraries shoud be controled in project build.config, as Android Studio / Gradle tries to read all setting in all flavors once and causes conflicts or other problems.
NB.3 Please keep in mind that Android Studio does not delete library(.so) files which are already sent to the device. It coufuses test results. Check files in /data/app/(package)/lib/(cpu arch)/ on device and (project)/app/build/intermediates/cmake/(flavor)/obj/(cpu arch)/ in your pc. Clear them manually, especially on the device ones, if you feel the result is wrong.
Clear data from app/storage setting will not clear libraries. Uninstall will clear them, so manual deletion of files is easier I think.
NB.4 Synchronize menu of Device File Explorer of Android Studio does not sync properly for lib or (cpu arch) directories. To see correct results, select /data or /data/app and sync.
Getting started with the new android studio it seems very flexible but that usually brings a steep learning curve. Hoping to get some help with that here.
I've built an app and deployed it with this build.gradle file
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "23.0.0"
defaultConfig {
applicationId "me.test.simpleProject
minSdkVersion 10
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
signingConfigs {
release {
storeFile file("../my.keystore.jks")
storePassword System.getenv("and_ks_pw")
keyAlias System.getenv("and_ky_alias")
keyPassword System.getenv("and_k_pw")
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
debug {
debuggable true
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.2.1'
compile project(":Library:lib_my_lib_a")
compile project(":Library:lib_my_lib_b")
compile project(":Library:lib_my_lib_c")
}
This works pretty well and once I learn the syntax it'll be much easier to use than the previous system.
Now my question.
I would like to basically add a few product flavors.
I currently use google analytics to track clicks, usage stats, simple things like that.
In the past [before android studio] typically distribute my apps in a free/ paid version.
I would like to create say build flavors for:
google play free
google play paid
amazon free
amazon paid
The code for these apps should be mostly identical but they cannot be since, for example adding links to google play in your amazon apps gets you in a bit of trouble over there. So I will need to have unique classes across the build flavors.
I was reading some docs and watching google talks about setting up the build flavors but I am a bit confused.
Can someone help me define the folder structure for this project?
current application structure
test/
.gradle/
.idea/
app/
app/build
app/libs <-- empty
app/src
app/.gitignore
app/app.iml
app/build.gradle
app/proguard-rules.pro
build/
gradle/
Library/lib_a
Library/lib_b
Library/lib_c
.gitignore
build.gradle
gradle.properties
gradlew
gradlew.bat
local.properties
settings.gradle
test.iml
Can I place the new folders anywhere and how do I link them up with grade to properly build the variants?
The first thing you should do, is add these flavors to your gradle script, inside the android section:
productFlavors {
googleFree {
applicationId = "com.your.app.gfree"
}
googlePaid {
applicationId = "com.your.app.gpaid"
}
amazonFree {
applicationId = "com.your.app.afree"
}
amazonPaid {
applicationId = "com.your.app.apaid"
}
}
Note that you can define different package names for your app if needed, as well as some additional properties. Full details: Gradle Plugin - Build Variants
At this point you will have three different concepts in your app now:
Build types: Debug and Release
Product flavors: googleFree, googlePaid, amazonFree and amazonPaid
Build variants: Combination of build types and flavors
googleFreeDebug
googleFreeRelease
googlePaidDebug
googlePaidRelease
amazonFreeDebug
amazonFreeRelease
amazonPaidDebug
amazonPaidRelease
Flavor Specific resources:
For defining resources, you should have:
app
src
main
java
res
googlePaid
java
res
googleFree
java
res
amazonPaid
java
res
amazonFree
java
res
With that structure in place, you can place all your shared stuff under main, while the flavor specific stuff would live on each flavor's folder. Then, when building a specific variant of your app, android and gradle will resolve the right resources for you. If you are interested in more details, see Resource Merging.
Once the flavors have been added to your build.gradle file, if you sync your project, Android Studio will be able to pick your build variants and you may select these from the IDE:
To use flavors in your app you should have a structure like this:
app
src
flavor1
java
res
flavor2
java
res
main
java
res
You have to define the flavors in the build.gradle:
productFlavors {
flavor1 {
//
}
flavor2 {
//
}
}
Then you can have different dependencies using:
dependencies {
releaseCompile
debugCompile
flavor1Compile
flavor1DegubCompile
}
For example:
dependencies {
freeCompile 'com.google.android.gms:play-services:7.5.0'
}
How is it possible to identify the current flavor being compiled. I'm trying to add a file to compile only if I'm compiling a certain product flavor.
buildTypes {
android.applicationVariants.all { variant ->
variant.productFlavors.each() { flavor ->
if (flavor.name.equals(currentFlavorName)) {
The problem is that I can't seem to find where the currentFlavourName of the flavor which I am currently building is located.
just put the strings you want for flavor1 into:
src/flavor1/res/values/strings.xml
and the strings for flavor2 into:
src/flavor2/res/values/strings.xml
no need to put logic into your gradle file
Android uses a unique build process regarding your resources for different flavors and it is very easy to control.
if you set up your main source:
project-name
------------/app
---------------/src
-------------------/main
------------------------/res
----------------------------/values
------------------------/java
-------------------/development
-------------------------------/res
-----------------------------------/values
-------------------------------/java
-------------------/production
------------------------------/res
----------------------------------/values
------------------------------/java
This would be a bottom up approach from product flavor into main. Meaning if you have a strings.xml with items having the same name existing in development/res/values and have values that also exist in main/res/values/strings.xml these will be over written (and same would go for the production flavor) based on the build variant defined in your gradle file.
android {
productFlavors {
production {
applicationId "com.test.prod"
versionName "1.0.0"
}
development {
applicationId "com.test.dev"
versionName "1.0.0"
}
}
I don't know if exits a method to get the currentFlavor. I haven't found it yet.
A ugly solution can be
variant.productFlavors.each() { flavor ->
if (flavor.name.equals("flavor1")) {
//..........
}
}
However, if you want to be able to control which strings.xml you are using, you can achieve it in different ways.
First of all you can just define a xml file in your flavor folder.
app/src/main/res/values/ -> for common resources
app/src/flavor1/res/values -> resources for flavor1
app/src/flavor2/res/values -> resources for flavor2
This doesn't require any config in your build.gradle script.
A second option is to define a resource value using build.gradle.
Something like:
productFlavors {
flavor1 {
resValue "string", "app_name", "IRCEnterprise"
}
//....
}
Another option is to create some field in your BuildConfig class using this kind of script.
productFlavors {
flavor1 {
buildConfigField "String", "name", "\"MY FLAVOR NAME\""
}
}