android – How to solve build error for product flavor, when a referenced but unnecessary source/resource not found?-ThrowExceptions

Exception or error:

I have a project and it has 2 product flavors with their own directories:

build.gradle:

android {
    ...
    productFlavors {
            free {
                applicationId "com.sample.free"
                 buildConfigField "boolean", "free", "true"
            }
            paid {
                applicationId "com.sample"
                buildConfigField "boolean", "free", "false"
            }
    }
}

And I have a class (such as PaidOperationClass) which is only used at paid flavor. So, I put that class under src/paid/java directory. And I have another class(such as CommonClass) which is used by both flavors. So I put that under src/main/java directory:

src/main/  --> has CommonClass
src/free/
src/paid/  --> has PaidOperationClass

In CommonClass I have a method such as:

if(!BuildConfig.FREE) {
    PaidOperationClass.doSomeStuff();
}

So the PaidOperationClass is referenced(has an import) but never used by free build.

If I build the application for paid flavor everything works perfect. But if I try to build it for free flavor, it fails because referenced but unnecessary class is not found. How do you solve this without code (class/method) replication (such as putting a dummy PaidOperationClass under free flavor)? Are there any gradle options which ignores this kind of build errors while building?

Edit: I need a gradle solution which won’t need code replication. An annotation based custom script maybe, which removes unnecessary code for product flavors at compile time.

How to solve:

You can’t do it, because the import statement is inside the CommonClass and it is not able to resolve PaidOperationClass in the free flavor.

One way to achieve it is:

Create an empty class in Free flavor:

public class PaidOperationClass{

  public static void doSomeStuff(){
      //do nothing..    
  }
}

###

The easiest solution might be to create a class: IPaidOperations that is housed in the /src/main directory that the PaidOperationClass implements. Then just use the interface in the CommonClass.

###

You are referencing the paid package from the free package whether or not your use:

if(!BuildConfig.FREE) {
     PaidOperationClass.doSomeStuff();
}

So I suggest one of two options:

1.
So if you have a piece of code that is never used, why do you keep it there? Remove it and replace it with:

if(!BuildConfig.FREE) {
     // Do something within the imported packages.
}

Otherwise, you must import the paid package.

2.
Or have two free package versions

free_paid and free_no_paid and remove the reference to paid in the free_no_paid and use this when compiling without paid and use the free_paid package when compiling with paid.

###

Another solution is to create two CommonClasses – both in free and paid source set (but not in main) which contain code specific to particular flavor. For example:
src/free/java/.../CommonClass.java:

doSomeStuff() {
  doSomeFreeStuff();    
}

src/paid/java/.../CommonClass.java:

doSomeStuff() {
  doSomePaidStuff();    
}

In such case code located in main source set can reference CommonClass#doSomeStuff() no matter which flavor is built. Only one CommonClass for particular flavor will be compiled. In other words instead of using ifs manually you let gradle to choose appropriate class to compile.

You can also extract really common code (which is not dependent on anything specific to free nor paid) from CommonClass to abstract class ie. BaseCommonClass placed in main source set and let CommonClass extend it.

Leave a Reply

Your email address will not be published. Required fields are marked *