Est-il possible de déclarer une variable dans Gradle utilisable en Java?

417

Est-il possible de déclarer une variable dans Gradle utilisable en Java? En gros, je voudrais déclarer quelques vars dans le build.gradle et ensuite l'obtenir (évidemment) au moment de la construction. Tout comme une macro pré-processeur en C / C ++ ...

Un exemple de déclaration serait quelque chose comme ça ...:

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

Y a-t-il un moyen de faire quelque chose comme ça?

klefevre
la source

Réponses:

796

Générer des constantes 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"
        }
    }
}

Vous pouvez y accéder avec BuildConfig.FOO

Générez des ressources Android

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

Vous pouvez y accéder de la manière habituelle avec @string/app_nameouR.string.app_name

rciovati
la source
4
Non, mais vous pouvez également générer des ressources. J'ai mis à jour ma réponse, y compris cela.
rciovati
2
Super merci. Quelque chose que j'ai découvert est bien, c'est que vous pouvez spécifier des répertoires alternatifs pour les versions de débogage et de publication. Dans <project>/src/, si vous créez le fichier debug/res/values/strings.xmlet un autre fichier release/res/values/strings.xml, vous pouvez également définir des ressources pour les versions de débogage et de publication d'une manière légèrement plus propre.
elimirks
6
@rciovati est-il possible de faire la même chose sans le androidplugin? c'est à dire en utilisant simplement apply plugin java? Merci!
Zennichimaro
2
Comment puis-je créer des constantes pour différentes versions de build et types de build?
Jakob Eriksson
3
Est-il possible de définir l'un des champs, comme l'année en cours, et d'y accéder quel que soit le type de build choisi (release, debug, ...)?
développeur Android
102

Un exemple d'utilisation d'une clé d'application Api dans une application Android (Java et 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
    }
}

Utilisation en code java

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

BuildConfig.APP_KEY_1

Utilisation en code xml

<data android:scheme="@string/APP_KEY_2" />
Denis
la source
1
Si je peux ajouter, cette variable peut également être transmise lors de l'exécution. Surtout utile lors de l'exécution de tests avec une configuration différente. Utilisation./gradlew -PAppKey="1234" testdebug
Jaswanth Manigundan
1
Pour déclarer la même propriété pour chaque type de build, vous pouvez également utiliser le defaultConfigbloc: stackoverflow.com/a/51521146/321354
rciovati
Avez-vous un exemple de travail de la partie XML? dans un référentiel Github ou Gist. Ça ne marche pas pour moi, je ne peux pas faire référence@string/APP_KEY_2
voghDev
32

Exemple utilisant les propriétés du système, défini dans build.gradle, lu à partir de l'application Java (suivi de la question dans les commentaires):

Fondamentalement, en utilisant la testtâche dans build.gradle, avec la méthode de tâche de test systemPropertydéfinissant une propriété système transmise au moment de l'exécution:

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

Et voici le reste de l'exemple de code (que vous pourriez probablement déduire, mais qui est inclus ici de toute façon): il obtient une propriété système MY-VAR1, attendue au moment de l'exécution, définie sur 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);
  }
}

Cas de test: s'il MY-VARn'est pas défini, le test doit échouer:

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

Exécuter (remarque: le test réussit):

$ 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

J'ai trouvé que la partie délicate est en fait d'obtenir la sortie de gradle ... Donc, la journalisation est configurée ici (slf4j + logback), et le fichier journal affiche les résultats (alternativement, exécutez gradle --info cleanTest test; il existe également des propriétés qui obtiennent stdout à la console, mais, vous savez, pourquoi):

$ 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 vous commentez " systemProperty..." (qui, en fait, ne fonctionne que dans une testtâche), alors:

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

Pour être complet, voici la configuration logback ( 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> 

Des dossiers:

  • build.gradle
  • src/main/java/example/HelloWorld.java
  • src/test/java/example/HelloWorldTest.java
  • src/test/resources/logback-test.xml
Michael
la source
Notez qu'il s'agit d'une réponse directe à un commentaire dans la réponse acceptée, il s'écarte donc un peu de la question d'origine.
Michael
2
Puis-je obtenir en quelque sorte version = '0.0.1-SNAPSHOT'via le code Java?
Nom1fan
SystemProperty est uniquement disponible dans la tâche de test Gradle :(. Quelqu'un connaît-il une autre façon d'avoir une valeur de variable Gradle dans le code Java de la bibliothèque?
Stoycho Andreev
systemPropertyn'a vraiment de sens que pour les tests, donc je vois pourquoi ils l'ont fait de cette façon (ce n'est pas un oubli), mais en même temps, j'ai aussi essayé d'utiliser gradle pour des choses auxquelles il n'était pas destiné (comme une application DSL ) afin que je puisse identifier. Comme alternative, je recommanderais simplement de charger les propriétés à partir d'un fichier de propriétés (ou d'un service de configuration, etc.), car s'il n'est pas en mode "test", il s'agit du mode "production" et nécessite une logique d'application. (C'est la théorie, de toute façon.)
Michael
14

Vous pouvez créer un champ de configuration de configuration remplaçable via des variables d'environnement système lors de la génération:

Fallback est utilisé lors du développement, mais vous pouvez remplacer la variable lorsque vous exécutez la génération sur Jenkins ou un autre outil.

Dans votre application 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 sera disponible en tant que BuildConfig.SERVER_URL.

Boris Treukhov
la source
1
Merci pour cette réponse! J'ai essayé de trouver comment rendre une variable d'environnement visible à partir d'un fichier Android .java, et cela a très bien fonctionné!
Wayne Piekarski
Si vous souhaitez définir une variable booléenne, vous devez utiliser buildConfigField "boolean", "CI_BUILD", "$ {isCi}" ou buildConfigField "boolean", "CI_BUILD", "Boolean.parseBoolean (" + '"' + isCi + ' "'+") "si vous voulez échapper aux contrôles des peluches ( stackoverflow.com/questions/29889098/… )
android_dev
5

La réponse de rciovati est tout à fait correcte. Je voulais juste ajouter une autre information que vous pouvez également créer des variables pour chaque type de build dans la partie de configuration par défaut de votre build.gradle. Cela ressemblerait à ceci:

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

Cela vous permettra d'avoir accès à travers

BuildConfig.App_NAME

Je voulais juste noter ce scénario si vous voulez une configuration commune.

pointe
la source
3

J'utilise ce code et je travaille très 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
la source
Ce serait bien si vous spécifiez ce que vous avez modifié et quel impact cela a, résultant en votre solution de travail.
Badgy
2

Comment pouvez-vous insérer le résultat String de la fonction dans buildConfigField

Voici un exemple de date de construction dans un ensemble de formats lisibles par l'homme:

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

def buildDate = getDate()

defaultConfig {
    buildConfigField "String", "BUILD_DATE", "\"$buildDate\""
}
anil
la source
1

j'utilise

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

Il est basé sur la réponse de Dennis mais l'attrape à partir d'une variable d'environnement.

Marc
la source
0

Aucune des réponses ci-dessus ne m'a donné de directives, j'ai donc dû passer deux heures à apprendre les méthodes Groovy.

Je voulais pouvoir aller à l'encontre d'une production, d'un bac à sable et d'un environnement local. Parce que je suis paresseux, je ne voulais changer l'URL qu'à un seul endroit. Voici ce que j'ai trouvé:

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

Syntaxe alternative, car vous ne pouvez l'utiliser ${variable}qu'avec des guillemets doubles dans les méthodes Groovy.

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

Ce que j'ai eu du mal à comprendre, c'est que les chaînes doivent être déclarées comme des chaînes entourées de guillemets. En raison de cette restriction, je ne pouvais pas utiliser la référence API_HOSTdirectement, ce que je voulais faire en premier lieu.

Rickard Elimää
la source