¿Es posible declarar una variable en Gradle utilizable en Java?

417

¿Es posible declarar una variable en Gradle utilizable en Java? Básicamente, me gustaría declarar algunos vars en build.gradle y luego obtenerlos (obviamente) en el momento de la compilación. Al igual que las macros de preprocesador en C / C ++ ...

Un ejemplo de declaración sería algo así ...:

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

¿Hay alguna manera de hacer algo así?

Klefevre
fuente

Respuestas:

796

Generar constantes de Java

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"
        }
    }
}

Puedes acceder a ellos con BuildConfig.FOO

Generar recursos de Android

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

Puede acceder a ellos de la forma habitual con @string/app_nameoR.string.app_name

rciovati
fuente
44
No, pero también puedes generar recursos. He actualizado mi respuesta incluyendo eso.
rciovati
2
Genial gracias. Algo que he descubierto es que podría especificar directorios alternativos para las versiones de depuración y lanzamiento. En <project>/src/, si crea el archivo debug/res/values/strings.xmly otro archivo release/res/values/strings.xml, también podría establecer recursos para la depuración y publicación de compilaciones de una manera un poco más limpia.
Elimirks
66
@rciovati ¿es posible lograr lo mismo sin el androidcomplemento? es decir, solo usando apply plugin java? ¡Gracias!
Zennichimaro
2
¿Cómo puedo crear constantes para diferentes tipos de compilación y tipos de compilación?
Jakob Eriksson
3
¿Es posible establecer uno de los campos, como el año actual, y también alcanzarlo sin importar el tipo de compilación elegido (lanzamiento, depuración, ...)?
Desarrollador de Android
102

Un ejemplo de uso de una clave de aplicación Api en una aplicación de Android (Java y 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
    }
}

Uso en código java

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

BuildConfig.APP_KEY_1

Uso en código xml

<data android:scheme="@string/APP_KEY_2" />
Denis
fuente
1
Si puedo agregar, esta variable también se puede pasar en tiempo de ejecución. Principalmente útil cuando se ejecutan pruebas con diferentes configuraciones. Uso./gradlew -PAppKey="1234" testdebug
Jaswanth Manigundan
1
Para declarar la misma propiedad para cada tipo de compilación, también puede usar el defaultConfigbloque: stackoverflow.com/a/51521146/321354
rciovati
¿Tiene un ejemplo funcional de la parte XML? en un repositorio de Github o Gist. No me funciona, no puedo hacer referencia@string/APP_KEY_2
voghDev
32

Ejemplo usando las propiedades del sistema, establecido en build.gradle, leído desde la aplicación Java (siguiendo la pregunta en los comentarios):

Básicamente, usando la testtarea en build.gradle, con el método de tarea de prueba systemPropertyestableciendo una propiedad del sistema que se pasa en tiempo de ejecución:

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'
}

Y aquí está el resto del código de muestra (que probablemente podría inferir, pero se incluye aquí de todos modos): obtiene una propiedad del sistema MY-VAR1, que se espera que en el tiempo de ejecución se establezca en 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);
  }
}

Caso de prueba: si no MY-VARestá configurado, la prueba debería fallar:

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);
    }
}

Ejecutar (nota: la prueba está pasando):

$ 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

Descubrí que la parte difícil es realmente obtener la salida de gradle ... Entonces, el registro se configura aquí (slf4j + logback), y el archivo de registro muestra los resultados (alternativamente, ejecutar gradle --info cleanTest test; también hay propiedades que se pueden usar como estándar). la consola, pero ya sabes por qué):

$ 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

Si comenta " systemProperty..." (que, por cierto, solo funciona en una testtarea), entonces:

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

Para completar, aquí está el 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> 

Archivos:

  • build.gradle
  • src/main/java/example/HelloWorld.java
  • src/test/java/example/HelloWorldTest.java
  • src/test/resources/logback-test.xml
Miguel
fuente
Tenga en cuenta que esta es una respuesta directa a un comentario en la respuesta aceptada, por lo que se desvía un poco de la pregunta original.
michael
2
¿De alguna manera puedo obtener el version = '0.0.1-SNAPSHOT'código Java?
Nom1fan
SystemProperty solo está disponible en la tarea de prueba de gradle :(. ¿Alguien sabe alguna otra manera de tener un valor variable de gradle en el código Java de la biblioteca?
Stoycho Andreev
systemPropertyrealmente solo tiene sentido para las pruebas, así que veo por qué lo hicieron de esta manera (no es un descuido), pero al mismo tiempo, también intenté usar gradle para cosas para las que no estaba destinado (como una aplicación DSL ) para que pueda identificarme. Como alternativa, recomendaría simplemente cargar propiedades desde un archivo de propiedades (o servicio de configuración, etc.), porque si no está en modo "prueba", entonces está en modo "producción" y requiere lógica de aplicación. (Esa es la teoría, de todos modos.)
Michael
14

Puede crear el campo de configuración de compilación reemplazable mediante variables de entorno del sistema durante la compilación:

Fallback se usa durante el desarrollo, pero puede anular la variable cuando ejecuta la compilación en Jenkins u otra herramienta.

En su aplicación 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
        }
    } 

La variable estará disponible como BuildConfig.SERVER_URL.

Boris Treukhov
fuente
1
Gracias por esta respuesta! He estado tratando de averiguar cómo hacer que una variable de entorno sea visible desde un archivo .java de Android, ¡y funcionó muy bien!
Wayne Piekarski
Si desea definir una variable booleana, debe usar buildConfigField "boolean", "CI_BUILD", "$ {isCi}" o buildConfigField "boolean", "CI_BUILD", "Boolean.parseBoolean (" + '"' + isCi + ' "'+") "si desea escapar de las comprobaciones de pelusas ( stackoverflow.com/questions/29889098/… )
android_dev
5

La respuesta de rciovati es completamente correcta. Solo quería agregar un tidbit más que también puede crear variables para cada tipo de compilación dentro de la parte de configuración predeterminada de su build.gradle. Esto se vería así:

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

Esto le permitirá tener acceso a través de

BuildConfig.App_NAME

Solo quería tomar nota de este escenario también si desea una configuración común.

espiga
fuente
3

Estoy usando este código y estoy trabajando muy bien.

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
    }
 }
}

}

Hitesh Sapra
fuente
Sería bueno si especificas qué modificaste y qué impacto tiene, lo que resulta en tu solución de trabajo.
Badgy 01 de
2

¿Cómo puede insertar el resultado de la función String en buildConfigField?

Aquí hay un ejemplo de fecha de compilación en un conjunto de formatos legibles por humanos:

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

def buildDate = getDate()

defaultConfig {
    buildConfigField "String", "BUILD_DATE", "\"$buildDate\""
}
añil
fuente
1

Estoy usando

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

Se basa en la respuesta de Dennis, pero la toma de una variable de entorno.

Bagazo
fuente
0

Ninguna de las respuestas anteriores me dio pautas, por lo que tuve que pasar dos horas aprendiendo sobre los métodos Groovy.

Quería poder ir en contra de un entorno de producción, sandbox y local. Como soy vago, solo quería cambiar la URL en un lugar. Esto es lo que se me ocurrió:

 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"
        }
    }

Sintaxis alternativa, porque solo puede usar ${variable}comillas dobles en 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"
    }

Lo que me resultó difícil de entender fue que las cadenas deben declararse como cadenas rodeadas de comillas. Debido a esa restricción, no pude usar la referencia API_HOSTdirectamente, que era lo que quería hacer en primer lugar.

Rickard Elimää
fuente