Android gradle buildTypes: Duplicate class - android

I'm converting my app to use gradle, and I'm trying to use the buildTypes. I have a Constants class which I wish to modify for my release build. So I have a file at src/main/java/my/package/name/Constants.java and at src/release/java/my/package/name/Constants.java.
When I try to build this, gradle tells me the build failed on the Constants file in my release buildtype, with the error that it's a duplicate class.
I also tried adding a different sourceSet for this in my build.gradle like this:
sourceSets {
main {
java.srcDirs = ['src/main/java'];
//...
}
release {
java.srcDirs = ['src/release/java'];
}
}
But this still gives me the same error. So I'm wondering, what am I doing wrong here?

You can not have a class in main and release. You need to split it into something like debug and release.
gradle will merge the source sets for each buildType with main.
This is the reason, why the class gets duplicated in your release build.
So the rule is: put a class into main, or in every buildType but not both.

The answer from "fix" helped me on the way, but I got an error from the main Gradle, saying a constant was missing (in my class Config). This since I had my class only in paid and free version and not in main. Could not find the Config class.
Im not sure if this is a bug in Studio... I finally solved it with the following:
buildTypes {
release {
...
buildConfig "public static final boolean XYZ = false;"
}
}
And the instead of using my Config.XYZ class constant I used buildConfig.XYZ

Related

Where are build types of the app defined?

I know we can edit build types in Android Studio:
I know we can edit each build type setting in gradle:
android {
buildTypes {
release {
minifyEnabled true
}
}
I know we can detect build types in code. How do I detect if I am in release or debug mode?
But where actually are the build types defined? Let say I want to commit it to git. What should I do to keep build types of the project consistent?
Where actually are the build types defined?
Basically, BuildConfig is the auto-generated class that resides under path :
app/build/generated/source/buildConfig/yourBuildType/yourPackageName/BuildConfig.java.
This class holds variables provided by buildTypes {} block from app level build.gradle file. So, on every clean & rebuild of project, Gradle auto generates BuildConfig class that can be used in further Android development environment.
I.e. BuildConfig.DEBUG is the default variable that we can use in our application code to determine it's buildType.
We can provide our own fields through buildType from build.gradle file like following:
android {
. . .
buildTypes {
debug {
buildConfigField "String", "SOME_VARIABLE", '"This string value is from build config class"'
}
}
. . .
}

How to check gradle build variant (debug or release) from dependency in Android project?

It is possible to set a variable in debug or release of buildTypes of app module. This doc has explained how to do that, Android: Managing different server URL for development and release.
However, my problem is different slightly. I have Project_A which is dependency to my App_Module. A class on my Project_A needs to know this build is Debug or Release. I created a variable in buildTypes based on what above doc's said (in App_Module). However, the variable seems is not visible to this dependency (Project_A).
I have following code in a class of Project_A:
if (BuildConfig.DEBUG)
{
MyConstants.URL_BASE = "https://my.debug.com";
}
else
{
MyConstants.URL_BASE = "https://my.release.com";
}
When I check the package of BuildConfig, the package belongs to Project_A (and there is no sign of App_Module in dropdown list of auto import packages). So what is your recommendation? How can I check build variant from dependency?
I used following solution for my problem. I defined Debug variable in Application class of Project_A:
// Placeholder for the BuildConfig.DEBUG flag. Should be overwritten by the final application class
// with the correct value based on build variant (debug/release).
public static boolean DEBUG = true;
Then, I'm set it from Application class of App_Module.
// Setup the debug flag
PassengerLibApplication.DEBUG = BuildConfig.DEBUG;
Therefore, my above code will be changed to
if (Project_A_Application.DEBUG)
{
MyConstants.URL_BASE = "https://my.debug.com";
}
else
{
MyConstants.URL_BASE = "https://my.release.com";
}

Retrieve the Flavor and BuildType dependent class path within gradle script

I am attempting to generate JNI headers using the 'javah' command and to do so I need to pass the directory which contains the project .class files. Previously with ant this was a static location, but it is dynamic with buildTypes and productFlavors on gradle.
Is there a variable in gradle which represents the class path? For instance I want to retrieve build/intermediates/classes/debug/, build/intermediates/classes/dev/debug/, etc.
I am on gradle 1.2.3, I have attempted to use 'sourceSets.main.output.classesdir' but I get an error that 'output' could not be found on source set main.
I was unable to find a straightforward way to determine the buildType and productFlavor dynamically, but I found a solution via examining the task graph.
gradle.taskGraph.whenReady { taskGraph ->
if (taskGraph.hasTask(assembleRelease)) {
buildTypeClassPath = "build/intermediates/classes/release"
} else {
buildTypeClassPath = "build/intermediates/classes/debug"
}
}
I think this solution can be adapted to incorporate productFlavors by changing the check for hasTask(assembleRelease) into hasTask(assembleProdRelease)

Can't find R when I change packageNameSuffix

I use gradle to build app. And I add a suffix to the packageName of my debug version. Just as following:
buildTypes {
debug {
packageNameSuffix ".debug"
}
}
However, one of the libs I use can't work with this.
I think the lib uses code like this to get the R class:
drawable = Class.forName(this.context.getPackageName() + ".R$drawable");
And it throws java.lang.IllegalArgumentException: ResClass is not initialized.
The correct package for R is com.xxx.R$drawable. Since I add a suffix to the package, when the lib want to get the class using reflection it gets com.xxx.debug.R$drawable.
Is there any way to fix it? BTW I can't modify the code of the lib because it is a jar file.
Not sure if it can help you, I have seen a similar problem in different circumstances.
The R class is just a class, for example, when the R package name is different from the current app package name, import com.xxx.yyy.R helps.
Probably you can create a missing class as an ancestor of the class with the correct package name. This, of course, will break your non-debug build, so you will have to add this class for debug builds and remove it for non-debug ones.

When does ADT set BuildConfig.DEBUG to false?

In the newest version of ADT (r17) a generated constant was added BuildConfig.DEBUG that is set according to the build type. The problem I have is that it is never set to false, I expected it to change when doing "Android Tools -> Export Signed Application Package" but it hasn't for me.
So how do I change the build type?
Added a feature that allows you to run some code only in debug mode.
Builds now generate a class called BuildConfig containing a DEBUG
constant that is automatically set according to your build type. You
can check the (BuildConfig.DEBUG) constant in your code to run
debug-only functions
Currently you can get the correct behavior by disabling "Build Automatically", cleaning the project and then export via "Android Tools -> Export Signed Application Package". When you run the application BuildConfig.DEBUG should be false.
With Eclipse, I always disable "Build Automatically" option before Exporting the app in release. Then I clean the project and export. Otherwise it starts compiling in debug mode, and then the value of BuildConfig.DEBUG may be wrong.
With Android Studio, I simply add my own custom variable in the build.gradle:
buildTypes {
debug {
buildConfigField "Boolean", "DEBUG_MODE", "true"
}
release {
buildConfigField "Boolean", "DEBUG_MODE", "false"
}
}
When I build the project, the BuildConfig.java is generated as follows:
public final class BuildConfig {
// Fields from build type: debug
public static final Boolean DEBUG_MODE = true;
}
Then in my code I can use:
if (BuildConfig.DEBUG_MODE) {
// do something
}
I recommand to clean after switching debug/release build.
It doesn't work properly:
Issue 27940: BuildConfig.DEBUG is "true" for exported application package
It's disappointing that they sometimes release buggy features.
Check for imports, sometimes BuildConfig is imported from any class of library unintentionally. For example:
import io.fabric.sdk.android.BuildConfig;
In this case BuildConfig.DEBUG will always return false;
import com.yourpackagename.BuildConfig;
In this case BuildConfig.DEBUG will return your real build variant.
p.s I just copy this one from my answer here:BuildConfig.DEBUG always false when building library projects with gradle
It does work, but note that the code file never changes, even when exporting the signed file. The export process changes the value of this variable to false, which might give you the false impression that it is not working.
I tested this with logging statements like
if (com.mypackage.BuildConfig.DEBUG)
Log.d(TAG, location.getProvider() + " location changed");
When testing, my Log statements no longer produce any output.
From Preparing for Release:
Turn off logging and debugging
Make sure you deactivate logging and disable the debugging option
before you build your application for release. You can deactivate
logging by removing calls to Log methods in your source files. You can
disable debugging by removing the android:debuggable attribute from
the tag in your manifest file, or by setting the
android:debuggable attribute to false in your manifest file. Also,
remove any log files or static test files that were created in your
project.
Also, you should remove all Debug tracing calls that you added to your
code, such as startMethodTracing() and stopMethodTracing() method
calls.
More information is following the link.
The solution for me:
Project -> Build Automatically
Project -> Clean
Project -> Build
Project Export Android application
It's work in r20
I would want to propose a simple workaround if you use proguard during APK export.
Proguard provides a way to remove calls to specific functions in release mode. Any calls for debugging logs can be removed with following setting in proguard-project.txt.
# Remove debug logs
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** v(...);
}
And optimization setting in project.properties.
proguard.config=${sdk.dir}/tools/proguard/proguard-android-optimize.txt:proguard-project.txt
With this, you don't need to concern any unnecessary String computation passing to debug log to which #Jeremyfa pointed. The computations are just removed in release build.
So the workaround for BuildConfig.DEBUG uses the same feature of proguard like following.
public class DebugConfig {
private static boolean debug = false;
static {
setDebug(); // This line will be removed by proguard in release.
}
private static void setDebug() {
debug = true;
}
public static boolean isDebug() {
return debug;
}
}
And following setting in proguard-project.txt.
-assumenosideeffects class com.neofect.rapael.client.DebugConfig {
private static *** setDebug();
}
I would prefer using this to disabling the Build Automatically option, because this doesn't depend on the builder's individual IDE setting but is maintained as committed file which are shared among developers.
Does not work properly as far as I understood (Android issue 22241)
I had some trouble on a project (working with Eclipse), that constant was not set to true when exporting a signed APK of my project :(
Would love to hear it works though
a good way is creating your own class :
public class Log {
public static void d(String message) {
if (BuildConfig.DEBUG)
android.util.Log.d(
"[" + (new Exception().getStackTrace()[1].getClassName()) + "]",
"{" + (new Exception().getStackTrace()[1].getMethodName()) + "} "
+ message
);
}
}
will you check your app level build.gradle debuggable true for release build
buildTypes {
release {
debuggable true
}
}
instead you keep false or comment that line
buildTypes {
release {
//debuggable true
}
}
now you will get BuildConfig.DEBUG false for release build
I've seen some strange behavior that has to do with when the values in BuildConfig are set to their final values. This may have something to do with your issue.
The simple explanation is that default values are set initially before Proguard is run, then after Proguard runs, the BuildConfig file is regenerated with the proper values. However, Proguard has already optimized your code by this point and you have issues.
Here is a bug I created against Gradle. https://code.google.com/p/android/issues/detail?id=182449

Categories

Resources