É possível declarar uma variável no Gradle utilizável em Java?

417

É possível declarar uma variável no Gradle utilizável em Java? Basicamente, eu gostaria de declarar alguns vars no build.gradle e obtê-lo (obviamente) no momento da compilação. Assim como as macros de pré-processador em C / C ++ ...

Um exemplo de declaração seria algo assim ...:

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

Existe uma maneira de fazer algo assim?

Klefevre
fonte

Respostas:

796

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

Você pode acessá-los com BuildConfig.FOO

Gere recursos do Android

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

Você pode acessá-los da maneira usual com @string/app_nameouR.string.app_name

rciovati
fonte
4
Não, mas você também pode gerar recursos. Eu atualizei minha resposta, incluindo isso.
rciovati
2
Incrível, obrigado. Algo que eu descobri é bom: você pode especificar diretórios alternativos para as compilações de depuração e lançamento. Além disso <project>/src/, se você criar o arquivo debug/res/values/strings.xmle outro arquivo release/res/values/strings.xml, poderá definir recursos para a depuração e liberar compilações de uma maneira um pouco mais limpa.
22614 Elvisks
6
@rciovati é possível conseguir o mesmo sem o androidplugin? ou seja, apenas usando apply plugin java? obrigado!
Zennichimaro 26/05
2
Como posso criar constantes para diferentes tipos e tipos de construção?
Jakob Eriksson
3
É possível definir um dos campos, como o ano atual, e também alcançá-lo, independentemente do tipo de compilação escolhido (liberação, depuração, ...)?
desenvolvedor android
102

Um exemplo de uso de uma Api App Key em um aplicativo Android (Java e 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 no código java

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

BuildConfig.APP_KEY_1

Uso no código xml

<data android:scheme="@string/APP_KEY_2" />
Denis
fonte
1
Se posso acrescentar, essa variável também pode ser transmitida em tempo de execução. Principalmente útil ao executar testes com configurações diferentes. Use./gradlew -PAppKey="1234" testdebug
Jaswanth Manigundan
1
Para declarar a mesma propriedade para cada tipo de compilação você pode usar o defaultConfigbloco assim: stackoverflow.com/a/51521146/321354
rciovati
Você tem um exemplo de trabalho da parte XML? em um repositório Github ou Gist. Não está funcionando para mim, não posso fazer referência@string/APP_KEY_2
voghDev
32

Exemplo usando propriedades do sistema, configurada em build.gradle, lida no aplicativo Java (acompanhando a pergunta nos comentários):

Basicamente, usando a testtarefa in build.gradle, com o método de tarefa de teste systemPropertyconfigurando uma propriedade do sistema que é passada no tempo de execução:

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

E aqui está o restante do código de amostra (que você provavelmente poderia inferir, mas está incluído aqui de qualquer maneira): ele obtém uma propriedade do sistema MY-VAR1, que em tempo de execução deve ser definida como 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 teste: se MY-VARnão estiver definido , o teste deverá falhar:

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

Execução (nota: o teste está passando):

$ 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

Eu descobri que a parte mais complicada é realmente obter a saída do gradle ... Então, o log é configurado aqui (slf4j + logback), e o arquivo de log mostra os resultados (como alternativa, execute gradle --info cleanTest test; também existem propriedades que são desatualizadas). o console, mas, você sabe o porquê):

$ 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

Se você comentar " systemProperty..." (que, aliás, funciona apenas em uma testtarefa), então:

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

Para completar, aqui está o 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> 

Arquivos:

  • build.gradle
  • src/main/java/example/HelloWorld.java
  • src/test/java/example/HelloWorldTest.java
  • src/test/resources/logback-test.xml
Michael
fonte
Observe que essa é uma resposta direta a um comentário na resposta aceita e, portanto, se desvia um pouco da pergunta original.
22613 Michael
2
Posso de alguma forma obter version = '0.0.1-SNAPSHOT'via código Java?
Nom1fan
SystemProperty só está disponível na tarefa de teste Gradle :( Qualquer um sabe qualquer outra maneira de ter valor variável Gradle no código java da biblioteca.?
Stoycho Andreev
systemPropertyrealmente só faz sentido para os testes, então vejo por que eles fizeram dessa maneira (não é uma supervisão), mas, ao mesmo tempo, também tentei usar o gradle para coisas para as quais não se destinava (como um aplicativo DSL ) para que eu possa identificar. Como alternativa, eu recomendaria apenas carregar propriedades de um arquivo de propriedades (ou serviço de configuração, etc.), porque se não estiver no modo "teste", será no modo "produção" e requer lógica de aplicativo. (Essa é a teoria, de qualquer maneira.)
michael
14

Você pode criar um campo de configuração de configuração substituível por meio de variáveis ​​de ambiente do sistema durante a construção:

O fallback é usado durante o desenvolvimento, mas você pode substituir a variável ao executar a construção no Jenkins ou em outra ferramenta.

No seu aplicativo 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
        }
    } 

A variável estará disponível como BuildConfig.SERVER_URL.

Boris Treukhov
fonte
1
Obrigado por esta resposta! Eu tenho tentado descobrir como obter uma variável de ambiente visível a partir de um arquivo .java do Android, e isso funcionou muito bem!
Wayne Piekarski
Se você deseja definir uma variável booleana, use buildConfigField "boolean", "CI_BUILD", "$ {isCi}" ou buildConfigField "boolean", "CI_BUILD", "Boolean.parseBoolean (" + '"' + isCi + ' "'+") "se você quiser escapar das verificações de fiapos ( stackoverflow.com/questions/29889098/… )
android_dev 26/12/18
5

A resposta de rciovati está totalmente correta. Eu só queria adicionar mais um petisco, para que você também possa criar variáveis ​​para cada tipo de compilação na parte de configuração padrão do seu build.gradle. Isso ficaria assim:

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

Isso permitirá que você tenha acesso a

BuildConfig.App_NAME

Só queria anotar esse cenário também se você quiser uma configuração comum.

Espinho
fonte
3

Estou usando esse código e funcionando muito bem.

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
fonte
Seria bom se você especificar o que modificou e qual o impacto que isso tem, resultando na sua solução de trabalho.
Badgy 1/11/18
2

Como você pode inserir o resultado da função String em buildConfigField

Aqui está um exemplo de data de compilação no conjunto de formatos legíveis 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\""
}
anil
fonte
1

estou a usar

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

É baseado na resposta de Dennis, mas a captura de uma variável de ambiente.

Marc
fonte
0

Nenhuma das respostas acima me deu orientações, então tive que passar duas horas aprendendo sobre os Métodos Groovy.

Eu queria ir contra uma produção, sandbox e ambiente local. Por ser preguiçoso, só queria alterar o URL em um só lugar. Aqui está o que eu vim com:

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

Sintaxe alternativa, porque você só pode usar ${variable}com aspas duplas nos Métodos 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"
    }

O que foi difícil de entender foi que as strings precisam ser declaradas como strings entre aspas. Por causa dessa restrição, não pude usar a referência API_HOSTdiretamente, o que eu queria fazer em primeiro lugar.

Rickard Elimää
fonte