Build flavors for different version of same class

AndroidAndroid Gradle-Plugin

Android Problem Overview


I've got a project, structured like this:

project/
   |
   |---src/
        |---flavorA2/
        |      |
        |      |---java/
        |      |     |---com.abc.flavorA.mk2
        |      |                 |-----classA.java
        |      |                 |-----classB.java
        |      |---res/
        |      |---AndroidManifest.xml
        |
        |---main
        |      |---java/
        |      |     |---com.abc.flavorA
        |      |                 |-----classA.java
        |      |                 |-----classB.java
        |      |                 |-----classC.java
        |      |                 |-----classD.java
        |      |---res/
        |      |    |---drawable/
        |      |    |---layout/
        |      |    |---values/
        |      |         
        |      |---AndroidManifest.xml
        |
        |---flavorA

flavorA will use the source and assets from main completely while flavorA2 has some small changes in classA and classB and the package name is also changed to com.abc.flavorA.mk2.

I had the build.gradle file like this:

...
buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }
    productFlavors {
        flavorA2 {
            packageName "com.abc.flavorA.mk2"
            versionCode 2
            versionName "1.0.1"
        }

        flavorA {
            packageName "com.abc.flavorA"
        }
    }
...

I run the code by selecting the build variant to flavorA2. However the running results shows that the gradle still choose the classes (classA and classB) from main instead of using the changed version inside flavorA2.

Am I missing something here?

Android Solutions


Solution 1 - Android

Since you have the classes under 2 different packages, these are totally different classes. So the classes aren't replacing each other.

With flavors, you can't override class files. So, one way to accomplish what you want is move these classes out of main, and into flavorA.

So you would have something like this:

project/
   |
   |---src/
        |---flavorA2/
        |      |
        |      |---java/
        |      |     |---com.abc
        |      |                 |-----classA.java
        |      |                 |-----classB.java
        |      |---res/
        |      |---AndroidManifest.xml
        |
        |---main/
        |      |---java/
        |      |     |---com.abc.flavorA
        |      |                 |-----classC.java
        |      |                 |-----classD.java
        |      |---res/
        |      |    |---drawable/
        |      |    |---layout/
        |      |    |---values/
        |      |         
        |      |---AndroidManifest.xml
        |
        |---flavorA/
        |      |---java/
        |      |     |---com.abc
        |      |                 |-----classA.java
        |      |                 |-----classB.java

This way, whenever you pick a flavor, only one version of ClassA and ClassB will be visible.

Solution 2 - Android

In the main build variant, Class A is com.abc.flavorA.classA, and in flavorA2 it's com.abc.flavorA.mk2.classA. These are two different fully-qualified class names and therefore two different classes.

You can't really override entire classes in a flavor. Depending on what you want to do, you might want to look into the BuildConfig mechanism -- in short, this is a class that's generated by the build system which can have values or statements that vary depending on the build type and flavor. You can have runtime code that looks at constants in that class and varies its behavior.

See https://stackoverflow.com/questions/20678118/android-studio-update-0-4-0-could-not-find-buildconfig for more information on the syntax, but in brief, it looks like this:

productFlavors {
    flavor {
      buildConfigField "boolean", "MY_FLAG", "true"
    }
}

Solution 3 - Android

You need to specify sourceSets in your build file. You need to modify your directory structure to make it so that only the folder names are different, everything under the java directory should be the same, so remove the mk2 from the class name. I'm not sure if the syntax is entirely correct but it should look like this:

android {
    sourceSets {
        flavorA {
            java {
                srcDirs = ['src/flavorA/java']
            }
        }

        flavorA2 {
            java {
                srcDirs = ['src/flavorA2/java']
            }
        }
    }
}

Solution 4 - Android

Posting this answer here hoping some one might have to implement the same, In my case, I had two static constants where I want to assign different values based on the build variant. So, I have created an object and declared the constants then placed the object in both variants by creating different source sets.

> Say the variants are 'Variat_A' & 'Variant_B'

Variat_A/java/package_name.util.variantspec

object ConfigConstant {
    // Carousels
    /**
     * Changing PERCENT_SCALE_X_IN or PERCENT_SCALE_Y_IN should also change scale animation
     * percentages accordingly.
     */
    const val PERCENT_SCALE_X_IN = 0.10 // 10% percentage
    const val PERCENT_SCALE_Y_IN = 0.10 // 10% percentage
}

Variat_B/java/package_name.util.variantspec

object ConfigConstant {
    // Carousels
    /**
     * Changing PERCENT_SCALE_X_IN or PERCENT_SCALE_Y_IN should also change scale animation
     * percentages accordingly.
     */
    const val PERCENT_SCALE_X_IN = 0.15 // 15% percentage
    const val PERCENT_SCALE_Y_IN = 0.15 // 15% percentage
}

> Accessing from some other class

class VerticalCarouselAdapter() {
   companion object {
        /**
         * Changing PERCENT_SCALE_X_IN or PERCENT_SCALE_Y_IN should also change scale animation
         * percentages accordingly.
         */
        const val PERCENT_SCALE_X_IN = ConfigConstant.PERCENT_SCALE_X_IN
        const val PERCENT_SCALE_Y_IN = ConfigConstant.PERCENT_SCALE_Y_IN
  }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondumbfingersView Question on Stackoverflow
Solution 1 - AndroidHassan IbraheemView Answer on Stackoverflow
Solution 2 - AndroidScott BartaView Answer on Stackoverflow
Solution 3 - AndroidashishduhView Answer on Stackoverflow
Solution 4 - AndroidAnoop M MaddasseriView Answer on Stackoverflow