Ist es möglich, eine Variable in Gradle zu deklarieren, die in Java verwendet werden kann?

417

Ist es möglich, eine Variable in Gradle zu deklarieren, die in Java verwendet werden kann? Grundsätzlich möchte ich einige vars in der build.gradle deklarieren und sie dann (offensichtlich) zur Build-Zeit abrufen. Genau wie ein Pre-Prozessor-Makro in C / C ++ ...

Ein Beispiel für eine Erklärung wäre so etwas ...:

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

Gibt es eine Möglichkeit, so etwas zu tun?

klefevre
quelle

Antworten:

796

Generieren Sie Java-Konstanten

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

Sie können mit auf sie zugreifen BuildConfig.FOO

Generieren Sie Android-Ressourcen

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

Sie können wie gewohnt mit @string/app_nameoder darauf zugreifenR.string.app_name

rciovati
quelle
4
Nein, aber Sie können auch Ressourcen generieren. Ich habe meine Antwort einschließlich dieser aktualisiert.
Rciovati
2
Super, danke. Ich habe festgestellt, dass Sie alternative Verzeichnisse für die Debug- und Release-Builds angeben können. In <project>/src/, wenn Sie die Datei erstellen debug/res/values/strings.xmlund eine andere Datei release/res/values/strings.xml, können Sie Ressourcen für die Debug - und Release - Builds in eine etwas sauberen Weise eingestellt , wie gut.
Elimirks
6
@rciovati ist es möglich, dasselbe ohne das androidPlugin zu erreichen ? dh nur mit apply plugin java? Vielen Dank!
Zennichimaro
2
Wie kann ich Konstanten für verschiedene Build-Varianten und Build-Typen erstellen?
Jakob Eriksson
3
Ist es möglich, eines der Felder als aktuelles Jahr festzulegen und es auch zu erreichen, unabhängig davon, welcher Build-Typ ausgewählt wurde (Release, Debug, ...)?
Android-Entwickler
102

Ein Beispiel für die Verwendung eines API-App-Schlüssels in einer Android-Anwendung (Java und 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
    }
}

Verwendung in Java-Code

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

BuildConfig.APP_KEY_1

Verwendung in XML-Code

<data android:scheme="@string/APP_KEY_2" />
Denis
quelle
1
Wenn ich hinzufügen darf, kann diese Variable auch zur Laufzeit übergeben werden. Besonders nützlich, wenn Tests mit unterschiedlicher Konfiguration ausgeführt werden. Verwendung./gradlew -PAppKey="1234" testdebug
Jaswanth Manigundan
1
Um dieselbe Eigenschaft für jeden Build-Typ zu deklarieren, können Sie auch den defaultConfigBlock verwenden: stackoverflow.com/a/51521146/321354
rciovati
Haben Sie ein funktionierendes Beispiel für den XML-Teil? in einem Github-Repository oder Gist. Es funktioniert nicht für mich, ich kann nicht verweisen@string/APP_KEY_2
voghDev
32

Beispiel für die Verwendung von Systemeigenschaften, die in build.gradle festgelegt und aus der Java-Anwendung gelesen wurden (nach der Frage in den Kommentaren):

Grundsätzlich wird bei Verwendung der testAufgabe in build.gradlemit der Testaufgabenmethode systemPropertyeine Systemeigenschaft festgelegt, die zur Laufzeit übergeben wird:

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

Und hier ist der Rest des Beispielcodes (den Sie wahrscheinlich ableiten könnten, der hier aber trotzdem enthalten ist): Er erhält eine Systemeigenschaft MY-VAR1, die zur Laufzeit voraussichtlich auf Folgendes gesetzt wird 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);
  }
}

Testfall: Wenn MY-VARnicht festgelegt, sollte der Test fehlschlagen:

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

Ausführen (Hinweis: Test besteht):

$ 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

Ich habe festgestellt, dass der schwierige Teil tatsächlich die Ausgabe von gradle erhält ... Daher wird hier die Protokollierung konfiguriert (slf4j + logback), und die Protokolldatei zeigt die Ergebnisse an (alternativ run gradle --info cleanTest test; es gibt auch Eigenschaften, auf die stdout angewendet wird) die Konsole, aber Sie wissen, warum):

$ 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

Wenn Sie " systemProperty..." auskommentieren (was übrigens nur in einer testAufgabe funktioniert ), dann:

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

Der Vollständigkeit halber hier die 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> 

Dateien:

  • build.gradle
  • src/main/java/example/HelloWorld.java
  • src/test/java/example/HelloWorldTest.java
  • src/test/resources/logback-test.xml
Michael
quelle
Beachten Sie, dass dies eine direkte Antwort auf einen Kommentar in der akzeptierten Antwort ist und daher ein wenig von der ursprünglichen Frage abweicht.
Michael
2
Kann ich irgendwie version = '0.0.1-SNAPSHOT'über Java-Code kommen?
Nom1fan
SystemProperty ist nur in Gradle-Testaufgabe verfügbar :(. Kennt jemand eine andere Möglichkeit, Gradle-Variablenwert im Java-Code der Bibliothek zu haben?
Stoycho Andreev
systemPropertymacht wirklich nur Sinn zum Testen, also verstehe ich, warum sie es so gemacht haben (es ist kein Versehen), aber gleichzeitig habe ich auch versucht, gradle für Dinge zu verwenden, für die es nicht gedacht war (wie eine Anwendung DSL ) damit ich mich identifizieren kann. Als Alternative würde ich empfehlen, nur Eigenschaften aus einer Eigenschaftendatei (oder einem Konfigurationsdienst usw.) zu laden, denn wenn es sich nicht im "Test" -Modus befindet, ist es "Produktions" -Modus und erfordert Anwendungslogik. (Das ist sowieso die Theorie.)
Michael
14

Sie können ein Build-Konfigurationsfeld erstellen, das während des Builds über Systemumgebungsvariablen überschrieben werden kann:

Fallback wird während der Entwicklung verwendet, Sie können die Variable jedoch überschreiben, wenn Sie den Build auf Jenkins oder einem anderen Tool ausführen.

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

Die Variable wird als verfügbar sein BuildConfig.SERVER_URL.

Boris Treukhov
quelle
1
Vielen Dank für diese Antwort! Ich habe versucht herauszufinden, wie eine Umgebungsvariable in einer Android-Java-Datei sichtbar wird, und das hat großartig funktioniert!
Wayne Piekarski
Wenn Sie eine boolesche Variable definieren möchten, sollten Sie buildConfigField "boolean", "CI_BUILD", "$ {isCi}" oder buildConfigField "boolean", "CI_BUILD", "Boolean.parseBoolean (" + '"' + isCi + ') verwenden. "'+") ", wenn Sie Flusenprüfungen entgehen möchten ( stackoverflow.com/questions/29889098/… )
android_dev
5

Die Antwort von rciovati ist völlig richtig. Ich wollte nur noch einen Leckerbissen hinzufügen, mit dem Sie auch Variablen für jeden Build-Typ im Standard-Konfigurationsteil Ihres build.gradle erstellen können. Das würde so aussehen:

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

Auf diese Weise haben Sie Zugriff auf durch

BuildConfig.App_NAME

Ich wollte dieses Szenario auch notieren, wenn Sie eine gemeinsame Konfiguration wünschen.

Spitze
quelle
3

Ich benutze diesen Code und arbeite sehr gut.

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
quelle
Es wäre schön, wenn Sie angeben, was Sie geändert haben und welche Auswirkungen dies hat, was zu Ihrer Arbeitslösung führt.
Badgy
2

Wie können Sie das String-Ergebnis der Funktion in buildConfigField einfügen?

Hier ist ein Beispiel für das Erstellungsdatum in einem für Menschen lesbaren Format:

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

def buildDate = getDate()

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

Ich benutze

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

Es basiert auf Dennis 'Antwort , greift jedoch auf eine Umgebungsvariable zurück.

Marc
quelle
0

Keine der obigen Antworten gab mir Richtlinien, so dass ich zwei Stunden damit verbringen musste, etwas über Groovy-Methoden zu lernen.

Ich wollte in der Lage sein, gegen eine Produktion, eine Sandbox und eine lokale Umgebung vorzugehen. Da ich faul bin, wollte ich die URL nur an einer Stelle ändern. Folgendes habe ich mir ausgedacht:

 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, da Sie ${variable}in Groovy-Methoden nur doppelte Anführungszeichen verwenden können.

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

Für mich war es schwer zu verstehen, dass Zeichenfolgen als Zeichenfolgen deklariert werden müssen, die von Anführungszeichen umgeben sind. Aufgrund dieser Einschränkung konnte ich die Referenz nicht API_HOSTdirekt verwenden, was ich ursprünglich tun wollte.

Rickard Elimää
quelle