ChatGPT解决这个技术问题 Extra ChatGPT

Is it possible to declare a variable in Gradle usable in Java?

Is it possible to declare a variable in Gradle usable in Java ? Basically I would like to declare some vars in the build.gradle and then getting it (obviously) at build time. Just like a pre-processor macros in C/C++...

An example of declaration would be something like that ... :

android {
    debug {
        A_VAR_RETRIEVABLE_IN_JAVA = 42
    }
    release {
        A_VAR_RETRIEVABLE_IN_JAVA = 42+52
    }
}

Is there a way to do something like that ?


T
Top-Master

Here are two ways to pass value from Gradle to use in Java;

Generate Java Constants

android {
    buildTypes {
        debug {
            buildConfigField "int", "FOO", "42"
            buildConfigField "String", "FOO_STRING", "\"foo\""
            buildConfigField "boolean", "LOG", "true"
        }

        release {
            buildConfigField "int", "FOO", "52"
            buildConfigField "String", "FOO_STRING", "\"bar\""
            buildConfigField "boolean", "LOG", "false"
        }
    }
}

You can access them with BuildConfig.FOO

Generate Android resources

android {
    buildTypes {
        debug{
            resValue "string", "app_name", "My App Name Debug"
        }
        release {
            resValue "string", "app_name", "My App Name"
        }
    }
}

You can access them in the usual way with @string/app_name or R.string.app_name


Nope, but you can generate resources as well. I've updated my answer including that.
Awesome, thanks. Something that I have discovered is well is that you could specify alternate directories for the debug and release builds. In <project>/src/, if you create the file debug/res/values/strings.xml and another file release/res/values/strings.xml, you could set resources for the debug and release builds in a slightly cleaner manner as well.
@rciovati is it possible to achieve the same without the android plugin? i.e. just using apply plugin java? thanks!
How can I create constants for different build flavors and build types?
Is it possible to set one of the fields, as the current year, and also reach it no matter which build type is chosen (release, debug, ...) ?
J
Joman68

An example of usage an Api App Key in an Android application (Java and XML)

gradle.properties

AppKey="XXXX-XXXX"

build.gradle

buildTypes {
//...
    buildTypes.each {
        it.buildConfigField 'String', 'APP_KEY_1', AppKey
        it.resValue 'string', 'APP_KEY_2', AppKey
    }
}

Usage in java code

Log.d("UserActivity", "onCreate, APP_KEY: " + getString(R.string.APP_KEY_2));

BuildConfig.APP_KEY_1

Usage in xml code

<data android:scheme="@string/APP_KEY_2" />

Link to an example of Api App Key usage in an Android application

Using String Constants Generated by Gradle Build Configurations


If I may add, this variable can be passed on runtime as well. Mostly useful when running tests with different configuration. Use ./gradlew -PAppKey="1234" testdebug
To declare the same property for each build type you can use the defaultConfig block as well: stackoverflow.com/a/51521146/321354
Do you have a working example of the XML part? in a Github repository or Gist. It's not working for me, I can't reference @string/APP_KEY_2
m
michael

Example using system properties, set in build.gradle, read from Java application (following up from question in comments):

Basically, using the test task in build.gradle, with test task method systemProperty setting a system property that's passed at runtime:

apply plugin: 'java'
group = 'example'
version = '0.0.1-SNAPSHOT'

repositories {
    mavenCentral()
    // mavenLocal()
    // maven { url 'http://localhost/nexus/content/groups/public'; }
}

dependencies {
    testCompile 'junit:junit:4.8.2'
    compile 'ch.qos.logback:logback-classic:1.1.2'
}

test {
  logger.info '==test=='
  systemProperty 'MY-VAR1', 'VALUE-TEST'
}

And here's the rest of the sample code (which you could probably infer, but is included here anyway): it gets a system property MY-VAR1, expected at run-time to be set to VALUE-TEST:

package example;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloWorld {
  static final Logger log=LoggerFactory.getLogger(HelloWorld.class);
  public static void main(String args[]) {
    log.info("entering main...");
    final String val = System.getProperty("MY-VAR1", "UNSET (MAIN)");
    System.out.println("(main.out) hello, world: " + val);
    log.info("main.log) MY-VAR1=" + val);
  }
}

Testcase: if MY-VAR is unset, the test should fail:

package example;
...
public class HelloWorldTest {
    static final Logger log=LoggerFactory.getLogger(HelloWorldTest.class);
    @Test public void testEnv() {
        HelloWorld.main(new String[]{});
        final String val = System.getProperty("MY-VAR1", "UNSET (TEST)");
        System.out.println("(test.out) var1=" + val);
        log.info("(test.log) MY-VAR1=" + val);
        assertEquals("env MY-VAR1 set.", "VALUE-TEST", val);
    }
}

Run (note: test is passing):

$ gradle cleanTest test
:cleanTest
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test

BUILD SUCCESSFUL

I've found that the tricky part is actually getting the output from gradle... So, logging is configured here (slf4j+logback), and the log file shows the results (alternatively, run gradle --info cleanTest test; there are also properties that get stdout to the console, but, you know, why):

$ cat app.log
INFO Test worker example.HelloWorld - entering main...
INFO Test worker example.HelloWorld - main.log) MY-VAR1=VALUE-TEST
INFO Test worker example.HelloWorldTest - (test.log) MY-VAR1=VALUE-TEST

If you comment out "systemProperty..." (which, btw, only works in a test task), then:

example.HelloWorldTest > testEnv FAILED
    org.junit.ComparisonFailure at HelloWorldTest.java:14

For completeness, here is the logback config (src/test/resources/logback-test.xml):

<configuration>
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>app.log</file>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d %p %t %c - %m%n</pattern>
        </layout>
 </appender>
 <root level="info">
     <appender-ref ref="FILE"/>
</root>
</configuration> 

Files:

build.gradle

src/main/java/example/HelloWorld.java

src/test/java/example/HelloWorldTest.java

src/test/resources/logback-test.xml


Note that this is a direct response to a comment in the accepted answer, so it deviates a little from the original question.
Can I somehow get version = '0.0.1-SNAPSHOT' via Java code?
SystemProperty is only available in gradle test task :( . Anybody knows any other way to have gradle variable value in the java code of the library?
systemProperty really only makes sense for testing, so I see why they did it this way (it's not an oversight), but at the same time, I've also tried to use gradle for things it wasn't intended for (like an application DSL) so I can identify. As an alternative, I'd recommend just loading properties from a property file (or config service, etc), because if it's not in "test" mode, then it's "production" mode & requires application logic. (That's the theory, anyway.)
B
Boris Treukhov

You can create build config field overridable via system environment variables during build:

Fallback is used while developing, but you can override the variable when you run the build on Jenkins or another tool.

In your app build.gradle:

buildTypes {
        def serverUrl =  '\"' + (System.getenv("SERVER_URL")?: "http://default.fallback.url.com")+'\"'
        debug{
            buildConfigField "String", "SERVER_URL", serverUrl
        }
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            buildConfigField "String", "SERVER_URL", serverUrl
        }
    } 

The variable will be available as BuildConfig.SERVER_URL.


Thank you for this answer! I've been trying to work out how to get an environment variable to be visible from within an Android .java file, and this worked great!
If you want to define boolean variable, you should use buildConfigField "boolean", "CI_BUILD", "${isCi}" or buildConfigField "boolean", "CI_BUILD", "Boolean.parseBoolean(" + '"' + isCi + '"' + ")" if you want to escape lint checks (stackoverflow.com/questions/29889098/…)
s
s.pike

rciovati's answer is entirely correct I just wanted to add one more tidbit that you can also create variables for every build type within the default config portion of your build.gradle. This would look like this:

android {
    defaultConfig {
        buildConfigField "String", "APP_NAME", "\"APP_NAME\""
    }
}

This will allow you to have access to through

BuildConfig.App_NAME

Just wanted to make a note of this scenario as well if you want a common config.


H
Hitesh sapra

I'm using this code and working very fine.

def baseUrl = '\"http://patelwala.com/myapi/"'
def googleServerKey = '\"87171841097-opu71rk2ps35ibv96ud57g3ktto6ioio.apps.googleusercontent.com"'
android {
  buildTypes {
  release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        buildConfigField 'String', 'BASE_URL', baseUrl
        buildConfigField 'String', 'web_client_id', googleServerKey
    }
    releasedebug {
        initWith debug
        buildConfigField 'String', 'BASE_URL', baseUrl
        buildConfigField 'String', 'web_client_id' ,googleServerKey
    }
    debug {

        buildConfigField 'String', 'BASE_URL', baseUrl
        buildConfigField 'String', 'web_client_id', googleServerKey
    }
 }
}

}


It would be nice if you specify what you modified and what impact it has, resulting in your working solution.
R
Rickard Elimää

None of the above answers gave me any guidelines so I had to spend two hours learning about Groovy Methods.

I wanted be able to go against a production, sandbox and local environment. Because I'm lazy, I only wanted to change the URL at one place. Here is what I came up with:

 flavorDimensions 'environment'
    productFlavors {
        production {
            def SERVER_HOST = "evil-company.com"
            buildConfigField 'String', 'API_HOST', "\"${SERVER_HOST}\""
            buildConfigField 'String', 'API_URL', "\"https://${SERVER_HOST}/api/v1/\""
            buildConfigField 'String', 'WEB_URL', "\"https://${SERVER_HOST}/\""
            dimension 'environment'
        }
        rickard {
            def LOCAL_HOST = "192.168.1.107"
            buildConfigField 'String', 'API_HOST', "\"${LOCAL_HOST}\""
            buildConfigField 'String', 'API_URL', "\"https://${LOCAL_HOST}/api/v1/\""
            buildConfigField 'String', 'WEB_URL', "\"https://${LOCAL_HOST}/\""
            applicationIdSuffix ".dev"
        }
    }

Alternative syntax, because you can only use ${variable} with double quotes in Groovy Methods.

    rickard {
        def LOCAL_HOST = "192.168.1.107"
        buildConfigField 'String', 'API_HOST', '"' + LOCAL_HOST + '"'
        buildConfigField 'String', 'API_URL', '"https://' + LOCAL_HOST + '/api/v1/"'
        buildConfigField 'String', 'WEB_URL', '"https://' + LOCAL_HOST + '"'
        applicationIdSuffix ".dev"
    }

What was hard for me to grasp was that strings needs to be declared as strings surrounded by quotes. Because of that restriction, I couldn't use reference API_HOST directly, which was what I wanted to do in the first place.


M
Marc

I'm using

buildTypes.each {
    it.buildConfigField 'String', 'GoogleMapsApiKey', "\"$System.env.GoogleMapsApiKey\""
}

Its based on Dennis's answer but grabs its from an environment variable.


E
Eric Aya

How can you insert String result of function into buildConfigField

Here's an example of build date in human-readable format set:

def getDate() {
    return new SimpleDateFormat("dd MMMM yyyy", new Locale("ru")).format(new Date())
}

def buildDate = getDate()

defaultConfig {
    buildConfigField "String", "BUILD_DATE", "\"$buildDate\""
}

R
Rahul Shyokand

https://stackoverflow.com/a/17201265/12021422 Answer by @rciovati works

But make sure you rebuild the project to be able to remove the error from Android Studio IDE

I spent 30 minutes trying to figure out why the new property variables aren't accessible.

If the "Make Project" as marked with red color doesn't work then try the "Rebuild Project" Button as marked with green color.

https://i.stack.imgur.com/iHB9r.png


M
Mahozad

This is for Kotlin DSL (build.gradle.kts) usable both in Java and Kotlin:

buildTypes {
    getByName("debug") { // or simply  debug {  in newer version of Android Gradle Plugin (AGP)
        buildConfigField("Boolean", "isHappy", "true")
        buildConfigField("String", "favoriteSong", """"Black Forest"""")
        resValue("string", "myName", "Lind")
    }
}