web-dev-qa-db-fra.com

Android Studio: Gradle Product Flavors: définir des propriétés personnalisées

Je crée différentes versions de produit d'une application Android dans Gradle (Android Studio).

J'ai donc défini les saveurs de produits suivantes:

Android {

    project.ext.set("customer", "")
    project.ext.set("server", "")

    //Configuration happens here - code removed for readability

    buildTypes {

        debug {
            server = "test"
        }

        release {
            server = "release"
        }
    }

    //Available product flavors
    productFlavors {
        customerA{
            customer = "a"
        }
        customerB{
            customer = "b"
        }
        customerC{
            customer = "c"
        }
    }
}

Cependant, plus tard, lorsque j'accède à la propriété de projet définie "client" (dont la valeur est définie dans la saveur du produit que je suis en train de créer) dans l'une de mes tâches de construction, elle a toujours la valeur "c" même si je crée le client A ( dans ce cas, le client doit être "a" plutôt que "c"). Par exemple, j'exécute la tâche suivante plus tard:

preBuild << {
    println "Building customer: " + customer
}

et il imprime toujours:

Client du bâtiment: c

Donc je suppose qu'il y a un écrasement? Peut-être lié à la phase d'exécution de la configuration VS? Je ne sais pas comment/pourquoi, donc toute aide est grandement appréciée.

UPDATE : Sinon, cela m'amènerait déjà à déterminer le nom de l'arôme du produit (sans le nom du type de build qui lui est attaché) et le type de build (encore : sans le nom de l'arôme du produit qui lui a été ajouté) pendant la phase d'exécution de la construction du gradle.

Compte tenu de la configuration ci-dessus, les noms de saveur de produit attendus seraient: customerA, customerB et customerC.

22
AgentKnopf

Pendant la phase d'évaluation, Gradle exécute tout le code de votre bloc Android; il n'exécute pas seulement le code correspondant aux saveurs que vous souhaitez compiler. En fait, pendant la phase d'évaluation, il ne sait même pas vraiment quelles sont vos saveurs; il doit évaluer cela pour le découvrir.

Donc, vos trois lignes customer = "a", customer = "b", et customer = "c" sera exécuté.

C'est l'une des choses subtiles de Gradle qui le rend un peu difficile à apprendre.

J'ai donc expliqué pourquoi votre code ne fonctionne pas comme vous l'attendez, mais cette réponse est incomplète car je n'ai pas beaucoup dit quoi faire pour le faire fonctionner correctement, mais il est difficile de dire quoi faire parce que je Je ne sais pas ce que vous essayez d'accomplir. En général, je peux dire que vous devriez penser à essayer d'accomplir ce que vous voulez en utilisant des tâches définies par l'utilisateur et à configurer des dépendances intra-tâche pour vous assurer que les choses sont exécutées dans le bon ordre. Un gotcha avec Android Gradle builds est que même ces tâches ne sont pas définies avant la phase d'évaluation (il ne peut pas savoir de quelles tâches il a besoin pour construire toutes vos saveurs jusqu'à ce qu'il ait évalué le fichier de build et sait quelles sont ces saveurs), alors faites quelques SO sleuthing pour voir comment accrocher les choses sur Android Tâches de construction Gradle - vous devez configurer vos tâches à la fin de la phase d'évaluation après que le plugin Android a fait son travail.

18
Scott Barta

Un grand merci à Scott Barta, pour ses suggestions et pour avoir expliqué pourquoi ma solution n'a pas fonctionné (ce qui m'a également fait reconsidérer certaines choses). J'ai essentiellement trouvé différentes façons d'accomplir ce dont j'avais besoin.

À moins que ce que vous devez faire ne puisse être atteint en organisant simplement votre Android arbre de ressources basé sur les types de construction et les saveurs (c'est-à-dire via la convention), alors je recommanderais l'option 2. Bien que je garde option 1 à des fins de référence car elle couvre le sujet intéressant de l'extension de propriété productFlavor.

  1. Option basée sur les propriétés personnalisées: les saveurs de produits vous permettent de définir des propriétés personnalisées et donc d'étendre une saveur de produit. Un exemple est fourni ici par Xavier Ducrohet: https://stackoverflow.com/a/17708357/10415

Je vais proposer un exemple très simple et similaire comme indiqué ci-dessus, bien que dans mon cas, j'avais besoin d'une propriété String, plutôt que d'un booléen.

    // This class will be used to create our custom property
    class StringExtension {
      String value

      StringExtension (String value) {
        this.value = value
      }

      public void setValue(String value) {
        this.value = value
      }

      public String getValue() {
        return value
      }
    }

    Android {
      // Add our new property to each product flavor upon creation
      productFlavors.whenObjectAdded { flavor ->
        //I am suspecting the last argument is the default value
        flavor.extensions.create("myProperty", StringExtension , '')
      }

      // then we can set the value on the extension of any flavor object
      productFlavors {
        customerA{
          myProperty.value 'customerA'
        }
        customerB{
          myProperty.value 'customerB'
        }
      }
    }

    //Adds a custom action to the preBuild task
    preBuild << {
    //Iterate over all application variants. We name our application variant object "variant" as indicated by "variant ->"
        Android.applicationVariants.all { variant ->
    //Here we can iterate over the flavors of our variant, well call the flavor "flavor" as indicated by "flavor ->"
            variant.productFlavors.each { flavor ->
    //Access our custom property "customerName"
                println "Building customer" + flavor.customerName.value

            }
        }
    }

J'ai alors réalisé que ce qui précède était totalement inutile, car tout ce que je voulais, c'était le nom de ma saveur (sans le type de construction) et une fois que j'ai trouvé la propriété qui me donne le nom de ma saveur, j'ai pu tout changer du code ci-dessus comme suit:

  1. Utilisez simplement le nom de votre saveur comme nom du client en accédant à la propriété de saveur de produit déjà existante appelée "nom".

    Android {
    
      productFlavors {
        customerA{
        }
        customerB{
        }
      }
    }
    
    //Adds a custom action to the preBuild task
    preBuild << {
    //Iterate over all application variants. We name our application variant object "variant" as indicated by "variant ->"
        Android.applicationVariants.all { variant ->
    //Here we can iterate over the flavors of our variant, well call the flavor "flavor" as indicated by "flavor ->"
            variant.productFlavors.each { flavor ->
    //Access our product flavor name
                println "Building customer" + flavor.name
    
            }
        }
    }
    

Ce qui précède a également beaucoup plus de sens, car ma structure de répertoire pour Android Resources est nommée d'après les saveurs réelles.

Ce dernier m'a également conduit à ma solution finale pour la question initiale:

  1. Approche basée sur le répertoire des ressources

L'intention était de modifier un fichier dans le dossier xml de chaque client selon qu'il s'agit d'une version ou d'une version de débogage. Cela peut être réalisé par une structure de dossiers correspondante. Sur la base de la question d'origine, nous avons 3 clients, et chaque client a un débogage et une version de version. Les fichiers xml mentionnés ci-dessus sont différents pour chaque client et type de build. D'où la structure de répertoires suivante:

src/
  - customerA
    //Contains all relevant resource files specific to customer A
  - customerB
    //Contains all relevant resource files specific to customer B
  - customerC
    //Contains all relevant resource files specific to customer C

  - customerADebug
    //Contains debug server-settings file for customer A
  - customerBDebug
    //Contains debug server-settings file for customer B
  - customerCDebug
    //Contains debug server-settings file for customer C
  - customerARelease
    //Contains release server-settings file for customer A
  - customerBRelease
    //Contains release server-settings file for customer B
  - customerCRelease
    //Contains release server-settings file for customer C

Le contenu principal de chaque saveur de produit se trouvait donc dans le dossier portant le même nom que la saveur (customerA, customerB, etc., voir la première partie de l'extrait ci-dessus). Maintenant, ce fichier, différent selon qu'il s'agissait d'une version de débogage ou de version pour chaque client, est placé dans les dossiers appropriés tels que customerADebug -> contient un fichier avec les paramètres du serveur pour le mode de débogage, etc.

Et lorsque vous générez customerA par exemple, le fichier correct sera choisi si vous générez une version de débogage ou de version.

Pour répondre à la partie MISE À JOUR de mon message:

Nom de l'arôme du produit (sans buildType):

flavour.name (où la saveur est un productFlavor)

11
AgentKnopf

Les éléments suivants ont fonctionné pour moi pour ajouter des propriétés personnalisées aux saveurs du produit:

Android {
    // ...defaultConfig...

    productFlavors.whenObjectAdded { flavor ->
        // Add the property 'myCustomProperty' to each product flavor and set the default value to 'customPropertyValue'
        flavor.ext.set('myCustomProperty', 'customPropertyValue')
    }

    productFlavors {
        flavor1 {
        }
        flavor2 {
            myCustomProperty = 'alternateValue'
        }
    }
}

flavor1 a la valeur par défaut pour la propriété personnalisée, tandis que flavor2 a la valeur remplacée.

Voici un exemple comment accéder à la propriété personnalisée:

applicationVariants.all { variant ->
    // Get the 'myCustomProperty' property from the variant's productFlavor (it's a list, but there should only be one)
    def customProp = variant.productFlavors*.myCustomProperty[0]
}

Je suppose que la même chose pourrait être faite pour ajouter des propriétés personnalisées aux types de construction, mais je n'ai pas testé cela.

6
ashughes