Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gradle : how to use BuildConfig in an android-library with a flag that gets set in an app

My (gradle 1.10 and gradle plugin 0.8)-based android project consists of a big android-library that is a dependency for 3 different android-apps

In my library, I would love to be able to use a structure like this

if (BuildConfig.SOME_FLAG) {
    callToBigLibraries()
}

as proguard would be able to reduce the size of the produced apk, based on the final value of SOME_FLAG

But I can't figure how to do it with gradle as :

* the BuildConfig produced by the library doesn't have the same package name than the app
* I have to import the BuildConfig with the library package in the library
* The apk of an apps includes the BuildConfig with the package of the app but not the one with the package of the library.

I tried without success to play with BuildTypes and stuff like

release {
    // packageNameSuffix "library"
    buildConfigField "boolean", "SOME_FLAG", "true"
}
debug {
    //packageNameSuffix "library"
    buildConfigField "boolean", "SOME_FLAG", "true"
}

What is the right way to builds a shared BuildConfig for my library and my apps whose flags will be overridden at build in the apps?

like image 983
Lakedaemon Avatar asked Jan 26 '14 16:01

Lakedaemon


People also ask

How is BuildConfig generated?

There's a class called BuildConfig. java which is automatically generated by the build system. This class is updated automatically by Android's build system (like the R class). It already contains a static final boolean called DEBUG, which is normally set to true.

Where is BuildConfig in Android?

BuildConfig. java is generated automatically by Android build tools, and is placed into the gen folder.

What is BuildConfig debug in Android?

In recent versions of the Android Developer Tools (ADT) for Eclipse, there's a class called BuildConfig which is automatically generated by the build. This class is updated automatically by Android's build system (like the R class), and it contains a static final boolean called DEBUG, which is normally set to true.

Why are there two Gradle files in Android Studio?

Why are there two build.gradle files in an Android Studio project? Strict Mode Policy : A tool to catch the bug in the Compile Time. What is ProGuard? What is use in Android? Gradle allows buildConfigField lines to define constants. These constants will be accessible at runtime as static fields of the BuildConfig class.

How to build a Java library using Gradle?

You now have the project setup to build a Java library. To build the project, run the build task. You can use the regular gradle command, but when a project includes a wrapper script, it is considered good form to use it instead.

What is the use of buildconfigfield in Gradle?

BuildConfigField #. Gradle allows buildConfigField lines to define constants. These constants will be accessible at runtime as static fields of the BuildConfig class. This can be used to create flavors by defining all fields within the defaultConfig block, then overriding them for individual build flavors as needed.

How do I Sync my Android app with Gradle?

... ... ... ... Click Sync Now in the notification bar. To learn more about app signing, read Sign Your App . The following tips help make developing your Android app easier. At build time, Gradle generates the BuildConfig class so your app code can inspect information about the current build.


3 Answers

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.

like image 135
Phil Avatar answered Oct 20 '22 19:10

Phil


Update: With newer versions of the Android Gradle plugin publishNonDefault is deprecated and has no effect anymore. All variants are now published.

The following solution/workaround works for me. It was posted by some guy in the google issue tracker:

Try setting publishNonDefault to true in the library project:

android {
    ...
    publishNonDefault true
    ...
}

And add the following dependencies to the app project that is using the library:

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

This way, the project that uses the library includes the correct build type of the library.

like image 27
AllThatICode Avatar answered Oct 20 '22 18:10

AllThatICode


You can't do what you want, because BuildConfig.SOME_FLAG isn't going to get propagated properly to your library; build types themselves aren't propagated to libraries -- they're always built as RELEASE. This is bug https://code.google.com/p/android/issues/detail?id=52962

To work around it: if you have control over all of the library modules, you could make sure that all the code touched by callToBigLibraries() is in classes and packages that you can cleave off cleanly with ProGuard, then use reflection so that you can access them if they exist and degrade gracefully if they don't. You're essentially doing the same thing, but you're making the check at runtime instead of compile time, and it's a little harder.

Let me know if you're having trouble figuring out how to do this; I could provide a sample if you need it.

like image 22
Scott Barta Avatar answered Oct 20 '22 19:10

Scott Barta