Czy można zadeklarować zmienną w Gradle używaną w Javie?

417

Czy można zadeklarować zmienną w Gradle używaną w Javie? Zasadniczo chciałbym zadeklarować niektóre zmienne w build.gradle, a następnie uzyskać (oczywiście) w czasie kompilacji. Podobnie jak makra preprocesora w C / C ++ ...

Przykładem deklaracji może być coś takiego:

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

Czy istnieje sposób na zrobienie czegoś takiego?

klefevre
źródło

Odpowiedzi:

796

Generuj stałe 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"
        }
    }
}

Możesz uzyskać do nich dostęp za pomocą BuildConfig.FOO

Generuj zasoby Androida

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

Możesz uzyskać do nich dostęp w zwykły sposób za pomocą @string/app_namelubR.string.app_name

rciovati
źródło
4
Nie, ale możesz także generować zasoby. Zaktualizowałem swoją odpowiedź, w tym.
rciovati
2
Wielkie dzieki. Coś, co odkryłem, jest dobre, że możesz określić alternatywne katalogi dla kompilacji debugowania i wydania. W <project>/src/przypadku, jeśli utworzysz plik debug/res/values/strings.xmli inny plik release/res/values/strings.xml, możesz ustawić zasoby dla wersji debugowania i wydań również w nieco czystszy sposób.
elimirks
6
@ rciovati czy można osiągnąć to samo bez androidwtyczki? tj. po prostu używasz apply plugin java? dzięki!
Zennichimaro
2
Jak mogę utworzyć stałe dla różnych wersji i typów kompilacji?
Jakob Eriksson
3
Czy możliwe jest ustawienie jednego z pól jako bieżącego roku, a także osiągnięcie go bez względu na wybrany typ kompilacji (wydanie, debugowanie, ...)?
programista Androida
102

Przykład użycia klucza aplikacji Api w aplikacji na Androida (Java i 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
    }
}

Wykorzystanie w kodzie Java

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

BuildConfig.APP_KEY_1

Użycie w kodzie XML

<data android:scheme="@string/APP_KEY_2" />
Denis
źródło
1
Jeśli mogę dodać, tę zmienną można również przekazać w środowisku wykonawczym. Najbardziej użyteczny podczas uruchamiania testów z inną konfiguracją. Użyj./gradlew -PAppKey="1234" testdebug
Jaswanth Manigundan
1
Aby zadeklarować tę samą właściwość dla każdego typu kompilacji, możesz również użyć defaultConfigbloku: stackoverflow.com/a/51521146/321354
rciovati,
Czy masz działający przykład części XML? w repozytorium Github lub Gist. Dla mnie to nie działa, nie mogę się odwoływać@string/APP_KEY_2
voghDev
32

Przykład użycia właściwości systemowych ustawionych w build.gradle, odczytanych z aplikacji Java (w odpowiedzi na pytanie w komentarzach):

Zasadniczo, używając testzadania w build.gradle, z testową metodą zadania systemPropertyustawianą właściwość systemową przekazywaną w czasie wykonywania:

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

A oto pozostała część przykładowego kodu (który prawdopodobnie można wywnioskować, ale i tak jest tu uwzględniony): otrzymuje właściwość systemową MY-VAR1, która w czasie wykonywania powinna być ustawiona na 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: jeśli nie MY-VARjest ustawiony, test powinien zakończyć się niepowodzeniem:

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

Uruchom (uwaga: test się kończy):

$ 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

Przekonałem się, że trudną częścią jest uzyskanie danych wyjściowych z gradle ... Więc logowanie jest skonfigurowane tutaj (slf4j + logback), a plik dziennika pokazuje wyniki (alternatywnie, uruchom gradle --info cleanTest test; są też właściwości, które można ustawić na standardowe wyjście konsolę, ale wiesz dlaczego):

$ 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

Jeśli skomentujesz „ systemProperty...” (co przy okazji działa tylko w testzadaniu), to:

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

Dla kompletności, oto 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> 

Akta:

  • build.gradle
  • src/main/java/example/HelloWorld.java
  • src/test/java/example/HelloWorldTest.java
  • src/test/resources/logback-test.xml
Michał
źródło
Zauważ, że jest to bezpośrednia odpowiedź na komentarz w zaakceptowanej odpowiedzi, więc nieco odbiega od pierwotnego pytania.
Michael
2
Czy mogę w jakiś sposób uzyskać version = '0.0.1-SNAPSHOT'kod Java?
Nom1fan
SystemProperty jest dostępny tylko w zadaniu testowania stopniowego :(. Czy ktoś zna jakikolwiek inny sposób, aby mieć wartość zmiennej stopniowej w kodzie java biblioteki?
Stoycho Andreev
systemPropertynaprawdę ma sens tylko do testowania, więc rozumiem, dlaczego zrobili to w ten sposób (to nie jest przeoczenie), ale jednocześnie próbowałem również używać gradable do rzeczy, do których nie było przeznaczone (np. DSL aplikacji ), dzięki czemu mogę zidentyfikować. Alternatywnie polecam ładowanie właściwości z pliku właściwości (lub usługi konfiguracyjnej itp.), Ponieważ jeśli nie jest to tryb „testowy”, to jest to tryb „produkcyjny” i wymaga logiki aplikacji. (W każdym razie taka jest teoria.)
Michael
14

Podczas tworzenia kompilacji można utworzyć pole konfiguracji kompilacji, które można zastąpić za pomocą zmiennych środowiskowych systemu:

Podczas programowania używana jest funkcja rezerwowa, ale można zastąpić zmienną po uruchomieniu kompilacji na Jenkins lub innym narzędziu.

W Twojej aplikacji 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
        }
    } 

Zmienna będzie dostępna jako BuildConfig.SERVER_URL.

Boris Treukhov
źródło
1
Dziękuję za tę odpowiedź! Próbowałem znaleźć sposób, aby uzyskać zmienną środowiskową, która będzie widoczna z poziomu pliku .java Androida, i to zadziałało świetnie!
Wayne Piekarski
Jeśli chcesz zdefiniować zmienną boolean, powinieneś użyć buildConfigField „boolean”, „CI_BUILD”, „$ {isCi}” lub buildConfigField „boolean”, „CI_BUILD”, „Boolean.parseBoolean („ + ”„ + isCi + ” „” + „)”, jeśli chcesz uniknąć sprawdzania kłaczków ( stackoverflow.com/questions/29889098/… )
android_dev
5

Odpowiedź rciovati jest całkowicie poprawna. Chciałem tylko dodać jeszcze jeden smakołyk, że można również tworzyć zmienne dla każdego typu kompilacji w domyślnej części konfiguracyjnej twojego build.gradle. Wyglądałoby to tak:

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

Umożliwi to dostęp do poprzez

BuildConfig.App_NAME

Chciałem tylko zanotować ten scenariusz, jeśli chcesz wspólnej konfiguracji.

kolec
źródło
3

Używam tego kodu i działa bardzo dobrze.

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
źródło
Byłoby miło, gdybyś określił, co zmodyfikowałeś i jaki ma to wpływ, w wyniku czego powstaje działające rozwiązanie.
Badgy
2

Jak wstawić wynik ciągu funkcji do buildConfigField

Oto przykład daty kompilacji w zestawie formatów czytelnym dla człowieka:

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

def buildDate = getDate()

defaultConfig {
    buildConfigField "String", "BUILD_DATE", "\"$buildDate\""
}
indygowiec
źródło
1

używam

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

Opiera się na odpowiedzi Dennisa, ale pobiera ją ze zmiennej środowiskowej.

Marc
źródło
0

Żadna z powyższych odpowiedzi nie dała mi żadnych wskazówek, więc musiałem poświęcić dwie godziny na naukę o Groovy Methods.

Chciałem być w stanie konkurować z produkcją, piaskownicą i lokalnym środowiskiem. Ponieważ jestem leniwy, chciałem zmienić adres URL tylko w jednym miejscu. Oto, co wymyśliłem:

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

Alternatywna składnia, ponieważ można używać tylko ${variable}z podwójnymi cudzysłowami w 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"
    }

Trudno mi było pojąć, że ciągi należy zadeklarować jako ciągi otoczone cudzysłowami. Z powodu tego ograniczenia nie mogłem API_HOSTbezpośrednio korzystać z referencji , co przede wszystkim chciałem zrobić.

Rickard Elimää
źródło