Uso de Build Flavors: estructuración de carpetas de origen y build.gradle correctamente

166

Tenga en cuenta: Respuesta editada después de la Respuesta de Xavier

Estoy tratando de usar diferentes Build Flavors para un mismo proyecto de aplicación en Android Studio. Sin embargo, parece que me está costando mucho configurarlo para que funcione correctamente.

Pasos:

  1. Cree un nuevo proyecto de Android Studio, llamado 'Prueba'.
  2. Abra build.gradle * y agregue las siguientes líneas:

    productFlavors {
    flavor1 {
        packageName 'com.android.studio.test.flavor1'
        }
    flavor2 {
        packageName 'com.android.studio.test.flavor2'
        }
    }
  3. Después de reiniciar Android Studio, ahora veo 4 variantes de compilación en la sección Variantes de compilación. Lo que significa que hemos tenido éxito en la selección de los sabores del producto hasta ahora. ** **
  4. Creó una nueva carpeta de origen para flavour1 ; Sin embargo, no estoy seguro si lo estoy haciendo de la manera correcta. Así es como lo hice:

    • Tenga en cuenta que el nombre de mi paquete para este proyecto es: com.foo.test
    • Haga clic derecho en la srccarpeta, para flavour1, en realidad creé las carpetas individuales en el explorador, de una manera que la estructura es src/flavor1/java/com/foo/test/MainActivity.java.
    • Lo anterior funcionó bien, ya que la carpeta 'java' está en azul , lo que significa que el IDE sabe que es un directorio fuente activo. Además, el paquete se creó automáticamente. A pesar de esto, recibo una advertencia para la clase duplicada encontrada. Ver captura de pantalla aquí.
    • Para flavour2, intenté crear el paquete manualmente, pero la carpeta 'src' para flavour2 parece no estar en azul y, por lo tanto, las opciones son diferentes cuando se hace clic con el botón derecho, y 'Nuevo paquete' no está disponible para mi uso. Ver imagen aquí.
    • Tenga en cuenta que para flavour1, también creé un directorio 'res', que se vuelve azul, pero a pesar de eso, no ofrece la posibilidad de crear un archivo de recursos de Android o un directorio de recursos de Andorid, en caso de que quisiera usar diferentes resurge para diferentes sabores.

¿Estoy haciendo algo mal? ¿O me estoy perdiendo algo? Hágame saber si necesita más información.

* Mi proyecto parece tener dos archivos build.gradle. Uno ubicado en la raíz de la carpeta del proyecto (\ GradleTest), este está vacío. El segundo ubicado en la raíz de una subcarpeta de \ GradleTest, también etiquetado 'GradleTest' (GradleTest-GradleTest), este es el que ya tenía código cuando se abrió; por lo tanto, ese es el que edité.

** Verifiqué la configuración de Gradle y aparentemente Usar importación automática ya estaba habilitado. A pesar de esto, realizar cambios en el archivo build.gradle no actualiza automáticamente las variantes de compilación. Nota: También intenté usar Build - Rebuild Project, y / o Build - Make Project, no-go. Todavía tengo que cerrar el proyecto y volver a abrir para que los cambios surtan efecto.

daniel_c05
fuente
Tenga en cuenta que applicationIdahora es compatible en lugar de packageName.
Hamzeh Soboh

Respuestas:

220

Si ingresó a las preferencias de Studio, en la sección Gradle, puede habilitar la importación automática para su proyecto (habilitaremos esto de manera predeterminada más adelante). Esto permitirá que Studio vuelva a importar su build.gradle cada vez que lo edite.

Crear sabores no significa que va a utilizar código personalizado para ellos, por lo que no creamos las carpetas. Necesitas crearlos tú mismo.

Si miras mi charla IO , verás cómo mezclamos los valores de los sabores y el tipo de construcción para crear la variante.

Para la fuente Java:

src/main/java
src/flavor1/java
src/debug/java

se utilizan los 3 para crear una salida única. Esto significa que no pueden definir la misma clase.

Si desea tener una versión diferente de la misma clase en los dos sabores, deberá crearla en ambos sabores.

src/flavor1/java/com/foo/A.java
src/flavor2/java/com/foo/A.java

Y luego su código en src / main / java puede hacer

import com.foo.A

dependiendo del sabor seleccionado, se usa la versión correcta de com.foo.A.

Esto también significa que ambas versiones de A deben tener la misma API (al menos cuando se trata de la API utilizada por las clases en src / main / java / ...

Editar para que coincida con la pregunta revisada

Además, es importante poner la misma clase A solo en las carpetas de origen que son mutuamente excluyentes. En este caso, src / flavour1 / java y src / flavour2 / java nunca se seleccionan juntos, pero main y flavour1 sí.

Si desea proporcionar una versión diferente de una actividad con un sabor diferente, no la ponga en src / main / java.

Tenga en cuenta que si tenía 3 sabores y solo quería uno personalizado para sabor1, mientras que sabor2 y sabor3 compartían la misma actividad, podría crear una carpeta fuente común para esas otras dos actividades. Tiene total flexibilidad para crear nuevas carpetas de origen y configurar el conjunto de origen para usarlas.

En sus otros puntos:

Es normal que la segunda carpeta de origen de sabor no sea azul. Necesitas cambiar al segundo sabor para habilitarlo, y luego podrás crear paquetes y clases dentro. Hasta entonces, Studio no lo considera una carpeta fuente. Con suerte, mejoraremos esto en el futuro para que el IDE sea consciente de aquellos inactivos carpetas de origen .

Creo que también es normal que no pueda crear archivos de recursos en la carpeta res. El sistema de menús no se ha actualizado para manejar todas estas carpetas de recursos adicionales. Esto vendrá después.

Xavier Ducrohet
fuente
1
Agregué algunos elementos nuevos al final de mi respuesta, pero el duplicado tiene sentido. No puede tener la misma clase tanto en src / main / java como en src / flavour1 / java, ya que ambas se usan al seleccionar flavour1. En mi respuesta, observe cómo pongo la misma clase solo en sabor1 / java y sabor2 / java, ya que estos son exclusivos y nunca se habilitan juntos.
Xavier Ducrohet
Hola Xavier, ¿puedes darme una descripción más detallada de cómo puedo usar una versión diferente de una actividad en mis sabores? Tengo un proyecto de prueba en el que quiero usar diferentes versiones de mi MainActivity, pero en ambas aplicaciones (flavour1 y flavour2) solo existe la versión de main / java. Cuando no pongo MainActivity dentro de main / java, la aplicación se bloquea cuando la inicio.
JensJensen 05 de
@XavierDucrohet ¿qué tal tener diferentes recursos así como diferentes códigos basados ​​en sabores, pero tenerlos en diferentes módulos para que podamos incluir un módulo u otro según el sabor, sin tener que mezclar código y recursos en el mismo proyecto raíz? ¿Eso es compatible?
Valerio Santinelli
3
@ValerioSantinelli Puede hacer dependencias por sabor. UsoflavorCompile ...
Xavier Ducrohet
@XavierDucrohet Intenté lo que sugeriste pero no funciona como esperaba. Puede ver cómo se estructura mi proyecto allí: stackoverflow.com/q/24410995/443136
Valerio Santinelli
19

"Sabores de productos" en Android

A veces me han preguntado cómo trabajar con diferentes hosts, íconos o incluso nombres de paquetes, dependiendo de las diferentes versiones de la misma aplicación.

Hay muchas razones para hacer esto y una forma fácil de hacerlo: sabores de productos.

Puede definir en su script build.gradle este tipo de cosas que he descrito antes.

Sabores de productos Parte de este artículo está escrito pensando en sabores de productos, entonces, ¿qué son? Con respecto a la documentación de Android:

El sabor de un producto define una versión personalizada de la compilación de la aplicación por el proyecto. Un solo proyecto puede tener diferentes sabores que cambian la aplicación generada.

¿Cómo puedes definirlos? Debe escribir en su build.gradle qué sabores desea definir:

productFlavors {  
        ...
        devel {
            ...
        }

        prod {
            ...
        }
    }

Ahora, tendremos dos sabores diferentes de nuestra aplicación. Puede verificarlo en Android Studio también dentro de la pestaña Build Variants

Construir variantes

Múltiples nombres de paquetes

¿Qué sucede si desea tener instalada en su teléfono una aplicación con estado de desarrollo y otra para estado de producción? Como ya sabrás, solo puedes instalar una aplicación con el mismo nombre de paquete (si intentas instalar un nuevo APK con el mismo que está instalado en tu teléfono, intentará actualizarlo).

Lo único que debe hacer es definirlo en cada uno de los sabores de su producto:

android {  
    productFlavors {
        devel {
            applicationId "zuul.com.android.devel"
        }
        prod {
            applicationId "zuul.com.android"
        }
    }
}

Enviar solicitudes a varios hosts según el sabor Como antes, debe incluir algunos parámetros en el campo de configuración del sabor de su producto.

android {  
    productFlavors {
        devel {
            applicationId "zuul.com.android.devel"
            buildConfigField 'String', 'HOST', '"http://192.168.1.34:3000"'

        }

        prod {
            applicationId "zuul.com.android"
               buildConfigField 'String', 'HOST', '"http://api.zuul.com"'

        }
    }
}

Como ejemplo, trataremos de mostrarle cómo puede integrar esto con Retrofit para enviar solicitudes al servidor apropiado sin manejar qué servidor está señalando y según el sabor. En este caso, este es un extracto de la aplicación para Android Zuul:

public class RetrofitModule {

    public ZuulService getRestAdapter() {
        RestAdapter restAdapter = new RestAdapter.Builder()
                .setEndpoint(BuildConfig.HOST)
                .setLogLevel(RestAdapter.LogLevel.FULL)
                .build();
        return restAdapter.create(ZuulService.class);
    }

}

Como puede ver, solo tiene que usar BuildConfigclass para acceder a la variable que acaba de definir.

Cualquier variable disponible a través de su código La variable HOST no es la única que puede exponer en su código. Puedes hacerlo con lo que quieras:

prod {  
    applicationId "zuul.com.android"
    buildConfigField 'String', 'HOST', '"http://api.zuul.com"'
    buildConfigField 'String', 'FLAVOR', '"prod"'
    buildConfigField "boolean", "REPORT_CRASHES", "true"
}

Puede acceder a ellos de la siguiente manera:

BuildConfig.HOST  
BuildConfig.FLAVOR  
BuildConfig.REPORT_CRASHES  

Diferentes íconos por sabor Si desea tener diferentes íconos por sabor, para que pueda detectar visualmente cuál está abriendo (también puede hacerlo por el nombre ... ¡Pero no cabe en el espacio!), Solo tiene para definir nuevas estructuras de directorio para cada uno de los sabores.

En el ejemplo que acabo de usar, hay dos sabores: desarrollo y producción. Luego, podríamos definir dos nuevas estructuras de directorio para poder definir los recursos que queramos:

estructura

Esto funciona con otros tipos de recursos como strings.xml, integers.xml, arrays.xml, etc.

Configurar ajustes de firma

Para configurar manualmente las configuraciones de firma para su tipo de compilación de lanzamiento utilizando las configuraciones de compilación de Gradle:

1.Cree un almacén de claves. Un almacén de claves es un archivo binario que contiene un conjunto de claves privadas. Debe mantener su almacén de claves en un lugar seguro y protegido. 2. Crear una clave privada. Una clave privada representa la entidad que se identificará con la aplicación, como una persona o una empresa. 3. Agregue la configuración de firma al archivo build.gradle de nivel de módulo:

android {
...
defaultConfig {...}
signingConfigs {
    release {
        storeFile file("myreleasekey.keystore")
        storePassword "password"
        keyAlias "MyReleaseKey"
        keyPassword "password"
    }
}
buildTypes {
    release {
        ...
        signingConfig signingConfigs.release
    }
}

}

Genere un APK firmado:

Para generar un APK firmado, seleccione Compilar> Generar APK firmado en el menú principal. El paquete en app / build / apk / app-release.apk ahora está firmado con su clave de lanzamiento.

ref: https://developer.android.com/studio/build/build-variants.html#signing,http://blog.brainattica.com/how-to-work-with-flavours-on-android/

A-Droid Tech
fuente
7

Parece que necesita volver a cargar su proyecto después de agregar nuevos sabores build.gradle. Después de eso, verá 4 Variantes de compilación en la vista Variantes de compilación (puede acceder desde el borde izquierdo de la ventana).

Con respecto a los directorios de origen adicionales, parece que necesita crearlos a mano: src/flavor1/javay src/flavor2/java. Verá que cambiar el sabor en la vista "Variantes de compilación" cambiará los directorios de origen actualmente activos (el directorio es azul cuando es un directorio de origen activo )

Por último, "Gradle creará nuevos sourceSets para sus nuevos sabores" significa que Gradle va a crear los objetos android.sourceSets.flavor1y android.sourceSets.flavor2y se puede utilizar en su guión build.gradle. Pero esos objetos se crean dinámicamente, por eso no los ves en el build.gradle(te sugiero que leas esto: http://www.gradle.org/docs/current/userguide/tutorial_using_tasks.html Especialmente el 6.6: explica el creación de tareas dinámicas. Un script de gradle es un script maravilloso, por lo que le sugiero que se familiarice con groovy también)

ben75
fuente
2
Creo que la nota de importación es la Build VariantsVista, no lo noté.
Chris.Jenkins
2

Tuve el mismo problema cuando migré mi proyecto a Gradle. El problema era que la compilación no encontró la carpeta de recursos adecuada. Lo arreglé agregando esto debajo del elemento de Android en build.gradle:

sourceSets {
        main {
            res.srcDirs = ['myProject/res']
        }
    }
Tomer
fuente
0

Algo que es importante y que me bloqueó por un tiempo es que el nombre del sabor que debe coincidir con el paquete en lugar del paquete definido dentro de la definición del sabor en gradle. Por ejemplo:

src/flavor1/java/com/foo/A.java

coincidirá

productFlavors {
  flavor1 {
    packageName 'com.android.studio.test.foobar'
  }
}

pero

src/foobar/java/com/foo/A.java no se usará para la versión sabor1.

Bitrock
fuente
0

En gradle:

Para los tipos de compilación solo necesita:

buildTypes {
   release{
    //proguard, signing etc.
   }
   debug {
    //development
   }
  }
}

Y luego, para los sabores, agrega los que necesita

productFlavors {
    pro {
        applicationIdSuffix '.paid'
        buildConfigField 'boolean', 'PRO', 'true'
    }
    free {
        applicationIdSuffix '.free'
        buildConfigField 'boolean', 'PRO', 'false'
    }
}
TouchBoarder
fuente