BuildConfig.DEBUG always false when building library projects with gradle
Asked Answered
J

14

86

BuildConfig.DEBUG is not working (= logically set to false) when I run my app in debug mode. I use Gradle to build. I have a library project where I do this check. BuildConfig.java looks like this in the build debug folder:

/** Automatically generated the file. DO NOT MODIFY */
package common.myProject;

public final class BuildConfig {
    public static final boolean DEBUG = Boolean.parseBoolean("true");

}

and in the release folder:

public static final boolean DEBUG = false;

both in the library project and in the application project.

I tried to get around this by checking a variable which is set a class of my project. This class inherits from the library and starts on startup.

<application
        android:name=".MyPrj" ...

This leads to another problem: is that I use my DEBUG variable in a DataBaseProvider which runs before the application class, and it will not run properly due to this bug.

Jamison answered 24/11, 2013 at 15:29 Comment(2)
It is a normal behavior. Where is the issue? You have to switch between BuildVariantsElectuary
The BuildConfig file is generated correctly but at run time it is false. I am having the same issue.Irishman
A
53

This is expected behavior for this.

Library projects only publish their release variants for consumption by other projects or modules.

We're working at fixing this but this is non trivial and requires a significant amount of work.

You can track the issue at https://code.google.com/p/android/issues/detail?id=52962

Arm answered 16/12, 2013 at 18:18 Comment(6)
Workaround: instaed of BuildConfig.DEBUG create another boolean variable at lib-project's e.g. BuildConfig.RELEASE and link it with application's buildType. Details: gist.github.com/almozavr/d59e770d2a6386061fcbMercorr
The solution provided by DodoEnte in issue tracker works just fine, no need for a work-around.Anemology
That's no longer the case. There is a proper solution for that. See my answer for more information.Sloe
That's true but it has to be done manually, and doesn't scale very well with flavors. We want to make this more automatic in the future.Arm
@XavierDucrohet This is an unexpected and counter intuitive behavior. You should definitely try to fix it if you can.Theophany
What's expected here? It occurs for libraries that are within the project itself, so why can't it be set to true when I run the entire project in debug mode...Mcelroy
S
86

With Android Studio 1.1 and having also the gradle version at 1.1 it is possible:

Library

android {
    publishNonDefault true
}

App

dependencies {
    releaseCompile project(path: ':library', configuration: 'release')
    debugCompile project(path: ':library', configuration: 'debug')
}

Complete documentation can be found here http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Library-Publication

EDIT:

The issue has just been marked as fixed for the Android Studio Gradle Version 3.0. There you can just use implementation project(path: ':library') and it'll select the correct configuration automatically.

Sloe answered 20/3, 2015 at 9:32 Comment(7)
This way works. But there is a drawback: the ":library:assembleRelease" is called even through you are making the ":app:assembleDebug", and this will result in a longer building time.Peggypegma
Wow, they finally updated that page a little bit and they finally added this feature.Derisible
@Konica Longer Gradle build time is a small price to pay - it's convoluted and long times anyway!! This worked wonderfully! Well done!Theophany
We need to add the "App" part for each library we use? If so, that's quite annoying...Mcelroy
Nice solution, but if I press "invalidate and restart" on Android Studio it shows me an error: Error:Module ':library' has variant 'release' selected, but the module '':app'' depends on variant 'debug'.Furl
This should be the marked correct answer ;) magic happens here hehehe thanksLogic
but what if using AAR libraryAlienate
A
53

This is expected behavior for this.

Library projects only publish their release variants for consumption by other projects or modules.

We're working at fixing this but this is non trivial and requires a significant amount of work.

You can track the issue at https://code.google.com/p/android/issues/detail?id=52962

Arm answered 16/12, 2013 at 18:18 Comment(6)
Workaround: instaed of BuildConfig.DEBUG create another boolean variable at lib-project's e.g. BuildConfig.RELEASE and link it with application's buildType. Details: gist.github.com/almozavr/d59e770d2a6386061fcbMercorr
The solution provided by DodoEnte in issue tracker works just fine, no need for a work-around.Anemology
That's no longer the case. There is a proper solution for that. See my answer for more information.Sloe
That's true but it has to be done manually, and doesn't scale very well with flavors. We want to make this more automatic in the future.Arm
@XavierDucrohet This is an unexpected and counter intuitive behavior. You should definitely try to fix it if you can.Theophany
What's expected here? It occurs for libraries that are within the project itself, so why can't it be set to true when I run the entire project in debug mode...Mcelroy
K
51

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.

Kutchins answered 14/7, 2016 at 11:49 Comment(0)
N
8

This is like Phil's answer except it doesn't need the context:

private static Boolean sDebug;

/**
 * Is {@link BuildConfig#DEBUG} still broken for library projects? If so, use this.</p>
 * 
 * See: https://code.google.com/p/android/issues/detail?id=52962</p>
 * 
 * @return {@code true} if this is a debug build, {@code false} if it is a production build.
 */
public static boolean isDebugBuild() {
    if (sDebug == null) {
        try {
            final Class<?> activityThread = Class.forName("android.app.ActivityThread");
            final Method currentPackage = activityThread.getMethod("currentPackageName");
            final String packageName = (String) currentPackage.invoke(null, (Object[]) null);
            final Class<?> buildConfig = Class.forName(packageName + ".BuildConfig");
            final Field DEBUG = buildConfig.getField("DEBUG");
            DEBUG.setAccessible(true);
            sDebug = DEBUG.getBoolean(null);
        } catch (final Throwable t) {
            final String message = t.getMessage();
            if (message != null && message.contains("BuildConfig")) {
                // Proguard obfuscated build. Most likely a production build.
                sDebug = false;
            } else {
                sDebug = BuildConfig.DEBUG;
            }
        }
    }
    return sDebug;
}
Nordau answered 3/2, 2015 at 2:55 Comment(2)
According to this (blog.javia.org/static-the-android-application-package) blog post you should never call the currentPackageName method from any thread other than the activity thread (UI thread). Cool solution though.Lemmons
@Rolfツ Well you could use the application-context instead.Mcelroy
G
6

As a workaround, you can use this method, which uses reflection to get the field value from the app (not the library):

/**
 * Gets a field from the project's BuildConfig. This is useful when, for example, flavors
 * are used at the project level to set custom fields.
 * @param context       Used to find the correct file
 * @param fieldName     The name of the field-to-access
 * @return              The value of the field, or {@code null} if the field is not found.
 */
public static Object getBuildConfigValue(Context context, String fieldName) {
    try {
        Class<?> clazz = Class.forName(context.getPackageName() + ".BuildConfig");
        Field field = clazz.getField(fieldName);
        return field.get(null);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return null;
}

To get the DEBUG field, for example, just call this from your Activity:

boolean debug = (Boolean) getBuildConfigValue(this, "DEBUG");

I have also shared this solution on the AOSP Issue Tracker.

Gravitate answered 27/8, 2014 at 1:41 Comment(2)
@shkschneider what line? Can you post your exception?Gravitate
Might be useful to others: beware of the use of applicationIdSuffix in Gradle which would make the .BuildConfig class not reachable from this above code.Lintel
M
6

Not really the correct way to check if you are in debug flavor, but you can check if the app itself is debuggable via:

private static Boolean sIsDebuggable;

public static boolean isDebuggable(Context context) {
    if (sIsDebuggable == null)
        sIsDebuggable = (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
    return sIsDebuggable;
}

The default behavior of apps and libraries will match it perfectly.

If you need a better workaround, you can use this instead:

public static boolean isInDebugFlavour(Context context) {
    if (sDebugFlavour == null) {
        try {
            final String packageName = context.getPackageName();
            final Class<?> buildConfig = Class.forName(packageName + ".BuildConfig");
            final Field DEBUG = buildConfig.getField("DEBUG");
            DEBUG.setAccessible(true);
            sDebugFlavour = DEBUG.getBoolean(null);
        } catch (final Throwable t) {
            sDebugFlavour = false;
        }
    }
    return sDebugFlavour;
}
Mcelroy answered 21/6, 2016 at 9:51 Comment(0)
B
3

You can create your own BuildConfig class for each build type using gradle

public class MyBuildConfig
{
    public static final boolean DEBUG = true;
}

for /src/debug/.../MyBuildConfig.java and...

public class MyBuildConfig
{
    public static final boolean DEBUG = false;
}

for /src/release/.../MyBuildConfig.java

Then use:

if (MyBuildConfig.DEBUG)
    Log.d(TAG, "Hey! This is debug version!");
Benthos answered 19/10, 2014 at 15:8 Comment(1)
Does "..." for the packageName of the library ? If so, this doesn't seem to work. I can't access the class.Mcelroy
T
2

Here is another solution.

1) Create an interface

public interface BuildVariantDetector {

    boolean isDebugVariant();

}

2) Use this interface on Application class (Appplication module)

public class MyApplication extends Application implements BuildVariantDetector {

    @Override
    public boolean isDebugVariant() {
        return BuildConfig.DEBUG; //application (main module) Buildonfig
    }

}

3) And then in library module:

boolean debugVariant = ((BuildVariantDetector)getApplication()).isDebugVariant();
Tobey answered 6/5, 2015 at 12:59 Comment(2)
This doesn't work. BuildConfig.DEBUG is still false for me.Anaclitic
Simple and elegant solution. Just make sure you import the app module's BuildConfig not the library's. That's a very sneaky mistake.Extrapolate
M
1

We had the same problem. I came up with something like this:

We have a SDK (library) and a demo project, hierarchy looks like this:

Parent
  |
  + SDK (:SDK)
  |
  + DemoApp (:DemoApp)

For the demo app we have, were :SDK:jarjarDebug and :SDK:jarjarRelease are some specific tasks for :SDK that produce some post-processed jars:

dependencies {
    debugCompile tasks.getByPath(":SDK:jarjarDebug").outputs.files
    releaseCompile tasks.getByPath(":SDK:jarjarRelease").outputs.files
    ... more dependencies ...
}

This works even for multiple buildTypes built at once. Debugging is a bit difficult though. Please comment.

Mig answered 25/11, 2014 at 23:51 Comment(0)
C
1

In my case I was importing the wrong BuildConfig as my project has many library modules. The fix was to import the correct BuildConfig for my app module.

Cower answered 8/12, 2016 at 18:26 Comment(0)
C
1

This is my workaround: reflect BuildConfig of app module:

`public static boolean debug = isDebug();

private static boolean isDebug() {
    boolean result = false;
    try {
        Class c = Class.forName("com.example.app.BuildConfig");
        Field f = c.getField("DEBUG");
        f.setAccessible(true);
        result = f.getBoolean(c);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return result;
}`
Clemens answered 16/8, 2017 at 10:35 Comment(1)
You have used reflection, but this was not necessary. You can use flavors in the build.gradle.Melvin
H
0

You could try this on each of the projects buildTypes:

parent.allprojects.each{ project -> android.defaultConfig.debuggable = true}
Hegemony answered 11/2, 2014 at 14:26 Comment(3)
Can you please explain? Add it to the "debug" buildType only? And to each of the modules ? It gives me an error : Error:(31, 0) No such property: debuggable for class: com.android.build.gradle.internal.dsl.ProductFlavor_DecoratedMcelroy
The specs of the android gradle plugin have changed so this is no longer valid. The debuggable flag has been moved to the buildType and not the build config. I theory setting the debug signing should do the same trickHegemony
Can you please check it out and update the answer? If there is an easy workaround, I'd like to know about it.Mcelroy
H
0

Working with debuggable true in gradle file.

buildTypes {
  demo{
 debuggable true
    }
  live{
 debuggable true
    }
}
Hebraist answered 14/11, 2018 at 7:12 Comment(0)
T
0

BuildConfig.DEBUG is not reliable at all, Android has provided an internal flag that is globally available indicating if a build is in Debug or non-Debug mode

// kotlin:
(context.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0
// java:
getContext().getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0

will be true if it is in debug

Credits : https://medium.com/@elye.project/checking-debug-build-the-right-way-d12da1098120

Trouble answered 12/7, 2019 at 12:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.