The Problem:
I imagine my problem is quite common. I have a fairly large gradle code base, from which I produce customised versions using product flavors. These product flavors will often require a customised version of one or more classes from src\main\java
.
I have read the Gradle documentation and have also come across the following questions looking at the same issue:
Using Build Flavors - Structuring source folders and build.gradle correctly
Build flavors for different version of same class
I understand why you can't define the same class in src\main\java
and also in your flavors, however the solution of moving the class from src\main\java
into your product flavor has a fairly major drawback. When you move a class from src\main\java
into your latest flavor to customise it, you also need to move a copy of the original uncustomised version of that class into every other previous product flavor or they won't build anymore.
You may only need to tweak one or two different classes from the originals each time (and then have to re-distrubute those classes to the flavor directories), but over time the number of classes moved will build and the number remaining in src\main\java
will decrease every time you have to do this. Eventually most of the classes will be in the flavors (even though the majority will be copies of the originals) and src\main\java
will be almost empty, kind of defeating the purpose of the whole Gradle build structure.
Additionally you'll need to keep a "default" flavor that you can clone each time you start a new flavor, so you know you're starting with all classes as per your original code base.
My Initial Workaround:
Use fields in the BuildConfig to define if custom class should be used or not:
buildConfigField 'boolean', 'CUSTOM_ACTIVITY_X', 'true'
You can then use code such as:
final Intent intent = new Intent();
...
if (BuildConfig.CUSTOM_ACTIVITY_X) {
intent.setClass(ThisActivity.this, CustomActivityX.class);
} else {
intent.setClass(ThisActivity.this, DefaultActivityX.class);
}
startActivity(intent);
Every flavor will still need a copy CustomActivityX, but it can just be a dummy empty class in flavors where you know it will not be used. This means your default versions of the classes are always retained in src\main\java
.
An Improved Workaround:
While trying to get rid of the need for a dummy CustomActivityX in every other flavor, I've looked at using Class.forName()
.
For example:
final Class activityX;
if (BuildConfig.CUSTOM_ACTIVITY_X) {
try {
activityX = Class.forName("CustomActivityX");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} else {
activityX = DefaultActivityX.class;
}
final Intent intent = new Intent();
...
intent.setClass(ThisActivity.this, activityX);
startActivity(intent);
However this obviously results in "activityX may not have been initialized" when you try to use it, because of the try/catch
block.
How can this be overcome???