Aplicación de varios tipos basada en una biblioteca de varios tipos en Android Gradle

102

Mi aplicación tiene varios sabores para varios mercados de sistemas de facturación dentro de la aplicación.

Tengo una única biblioteca que comparte el código base de todos mis proyectos. Así que decidí agregar esos sistemas de pago a esta biblioteca como sabores de productos.

La pregunta es ¿puede la biblioteca de Android tener sabores de productos?

Si es así, ¿cómo puedo incluir diferentes sabores en el respectivo sabor de la aplicación?

Busqué mucho y no pude encontrar nada sobre este escenario. Lo único cercano que encontré fue esto en http://tools.android.com/tech-docs/new-build-system/user-guide :

dependencies {
    flavor1Compile project(path: ':lib1', configuration: 'flavor1Release')
    flavor2Compile project(path: ':lib1', configuration: 'flavor2Release')
}

Cambié la configuración a diferentes cosas, ¡pero no funcionó!

Estoy usando Android Studio 0.8.2.

Ali
fuente
después de muchas búsquedas, no encontré la manera de lograr esto, incluso actualicé el complemento de Android a la última 3.4.2versión y gradle a la última 5.5.1, aún falló con el tiempo de compilación o la vinculación de recursos falló en aapt, o no puedo encontrar el símbolo que está dentro de la biblioteca módulo
VinceStyling

Respuestas:

141

Finalmente descubrí cómo hacer esto, lo explicaré aquí para otros que enfrentan el mismo problema:

La parte clave es establecer publishNonDefault en true en la biblioteca build.gradle, luego debe definir las dependencias como lo sugiere la guía del usuario.

Todo el proyecto sería así:

Biblioteca build.gradle:

apply plugin: 'com.android.library'

android {        
    ....
    publishNonDefault true
    productFlavors {
        market1 {}
        market2 {}
    }
}

proyecto build.gradle:

apply plugin: 'com.android.application'

android {
    ....
    productFlavors {
        market1 {}
        market2 {}
    }
}

dependencies {
    ....
    market1Compile project(path: ':lib', configuration: 'market1Release')
    market2Compile project(path: ':lib', configuration: 'market2Release')
}

Ahora puede seleccionar el tipo de aplicación y el panel de Variantes de compilación y la biblioteca se seleccionará en consecuencia y toda la compilación y ejecución se realizará en función del tipo seleccionado.

Si tiene varios módulos de aplicaciones basados ​​en la biblioteca, Android Studio se quejará del conflicto de selección de variantes, está bien, simplemente ignórelo.

ingrese la descripción de la imagen aquí

Ali
fuente
Gracias por compartir, ahora puedo deshacerme de mi solución predeterminadaPublishConfig.
Delblanco
2
Al ejecutar AS 1.1.0, la solución anterior parece seguir funcionando, sin embargo 1) la opción de depuración / liberación de compilaciones se pierde y parece que sigo teniendo problemas con AIDL que se encuentran en la biblioteca y que no pueden producir el código apropiado muy a menudo. Tiene alguna idea sobre esto?
3c71
1
@IgorGanapolsky buildTypes no tiene nada que ver con esto. Cada sabor tiene todos los tipos de compilación (generalmente depuración y lanzamiento) y todos funcionan con este enfoque.
Ali
1
@ An-droid ¡define la biblioteca que se utilizará para el sabor market1!
Ali
1
¿Por qué se establece en el tipo de compilación "lanzamiento"? ¿Se elige el tipo de compilación de "lanzamiento" durante las compilaciones de depuración?
WindRider
35

Hay un problema con la respuesta de Ali . Estamos perdiendo una dimensión muy importante en nuestras variantes de construcción. Si queremos tener todas las opciones (en mi ejemplo a continuación 4 (2 x 2)) solo tenemos que agregar configuraciones personalizadas en el archivo build.gradle del módulo principal para poder usar todos los tipos de compilación múltiples de varios tipos en Build Variants. También tenemos que establecer publishNonDefault como verdadero en el archivo build.gradle del módulo de biblioteca .

Solución de ejemplo:

Lib build.gradle

android {

    publishNonDefault true

    buildTypes {
        release {
        }
        debug {
        }
    }
    productFlavors {
        free {
        }
        paid {
        }
    }
}

App build.gradle

android {

    buildTypes {
        debug {
        }
        release {
        }
    }
    productFlavors {
        free {
        }
        paid {
        }
    }
}

configurations {
    freeDebugCompile
    paidDebugCompile
    freeReleaseCompile
    paidReleaseCompile
}

dependencies {

    freeDebugCompile project(path: ':lib', configuration: 'freeDebug')
    paidDebugCompile project(path: ':lib', configuration: 'paidDebug')
    freeReleaseCompile project(path: ':lib', configuration: 'freeRelease')
    paidReleaseCompile project(path: ':lib', configuration: 'paidRelease')

}
AppiDevo
fuente
Después de hacer lo mismo en Mi aplicación Error:java.lang.RuntimeException: Error: more than one library with package name, ocurrió
Chetan Joshi
21

Actualización para el complemento de Android 3.0.0 y superior

De acuerdo con la documentación oficial de Android - Migre configuraciones de dependencia para módulos locales ,

Con la resolución de dependencias con reconocimiento de variantes, ya no es necesario utilizar configuraciones específicas de variantes, como freeDebugImplementation, para las dependencias de módulos locales; el complemento se encarga de esto por usted

En su lugar, debe configurar sus dependencias de la siguiente manera:

dependencies {
    // This is the old method and no longer works for local
    // library modules:
    // debugImplementation project(path: ':library', configuration: 'debug')
    // releaseImplementation project(path: ':library', configuration: 'release')

    // Instead, simply use the following to take advantage of
    // variant-aware dependency resolution. You can learn more about
    // the 'implementation' configuration in the section about
    // new dependency configurations.
    implementation project(':library')

    // You can, however, keep using variant-specific configurations when
    // targeting external dependencies. The following line adds 'app-magic'
    // as a dependency to only the "debug" version of your module.

    debugImplementation 'com.example.android:app-magic:12.3'
}

Entonces, en la respuesta de Ali, cambia

dependencies {
    ....
    market1Compile project(path: ':lib', configuration: 'market1Release')
    market2Compile project(path: ':lib', configuration: 'market2Release')
}

a

implementation project(':lib')

Y el complemento se encargará de las configuraciones específicas de variantes automáticamente. Espero que ayude a otros a actualizar Android Studio Plugin a 3.0.0 y superior.

Harsh4789
fuente
7

Mi complemento de Android es 3.4.0, y encuentro que no necesita configuraciones ahora. Todo lo que necesita es asegurarse de que el flavourDimensions y productFlavors en la aplicación contenga un productFlavor del mismo flavourDimensions y productFlavors en las bibliotecas. Para muestra:

En el build.gradle de mylibrary

apply plugin: 'com.android.library'

android {        
    ....
    flavorDimensions "mylibFlavor"

    productFlavors {
        market1
        market2
    }
}

build.gradle de la aplicación:

apply plugin: 'com.android.application'

android {
    ....
    flavorDimensions "mylibFlavor", "appFlavor"
    productFlavors {
        market1 {
            dimension "mylibFlavor"
        }
        market2 {
            dimension "mylibFlavor"
        }
        common1 {
            dimension "appFlavor"
        }
        common2 {
            dimension "appFlavor"
        }
    }
}

dependencies {
    ....
    implementation project(path: ':mylibrary')
}

Después de la sincronización, puede cambiar todas las opciones en la ventana Build Variants: ingrese la descripción de la imagen aquí

JiajiaGu
fuente
Pero, ¿qué pasa si no quiero tener los mismos sabores en mi módulo de aplicación principal? Supongamos que tengo varios módulos de aplicación que tienen sus propios sabores específicos y un módulo común que tiene sus propios sabores y quiero usar en mi aplicación mi biblioteca con un sabor específico. ¿Cómo lo harías tú? No tiene sentido copiar mis versiones de lib a todas las aplicaciones.
Billda
@Billda No es necesario copiar todo, solo mantener un mismo productFlavor en la aplicación, para mi muestra, puedo mantener market1 o market2 en build.gradle de la aplicación.
JiajiaGu
2

Para que los sabores funcionen en una biblioteca AAR, debe definir defaultPublishConfig en el archivo build.gradle de su módulo de biblioteca de Android.

Para obtener más información, consulte: Publicación de la biblioteca .

Publicación de la biblioteca

De forma predeterminada, una biblioteca solo publica su variante de lanzamiento. Esta variante será utilizada por todos los proyectos que hagan referencia a la biblioteca, sin importar la variante que ellos mismos construyan. Esta es una limitación temporal debido a las limitaciones de Gradle que estamos trabajando para eliminar. Puede controlar qué variante se publica:

android {defaultPublishConfig "debug"}

Tenga en cuenta que este nombre de configuración de publicación hace referencia al nombre completo de la variante. La liberación y la depuración solo se aplican cuando no hay sabores. Si quisiera cambiar la variante publicada predeterminada mientras usa sabores, escribiría:

android {defaultPublishConfig "fragrance1Debug"}

David Lev
fuente
1

Por el momento no es posible, aunque si mal no recuerdo es una característica que quieren agregar. (Editar 2: enlace , enlace2 )

Editar: por el momento estoy usando la defaultPublishConfigopción para declarar qué variante de biblioteca se publica:

android {
    defaultPublishConfig fullRelease
    defaultPublishConfig demoRelease 
}
Delblanco
fuente
1
Entonces, cada vez que voy a compilar la aplicación, ¿debo cambiar esto en build.gradle de la biblioteca?
Ali
Bueno, sí ... cada vez que quieras compilar la aplicación con un sabor diferente.
Delblanco
En realidad, cuando defino sabores para el módulo de biblioteca, el paquete R heredado no se encuentra en el módulo de la aplicación.
Ali
¿Sincronizó los archivos gradle en AS?
Delblanco
@Delblanco Esto parece un trabajo manual y muy frágil (los desarrolladores son vagos y se olvidan de modificar sus archivos build.gradle).
IgorGanapolsky
1

Sé que este tema se ha cerrado, pero solo una actualización con gradle 3.0, vea esto: https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html#variant_aware y grep matchingFallbacksy missingDimensionStrategy. Ahora es mucho más sencillo declarar las dependencias entre los tipos de módulos.

... y en este caso preciso con gradle3.0, como los sabores comparten el mismo nombre, gradle los mapearía mágicamente, no se requiere configuración.

AlexG
fuente
Para mí, parece que se omiten las cosas generadas en tiempo de ejecución. Como ejemplo, simonvt-> la generación de esquemas ya no funciona con la nueva forma para mí. : - /
Stefan Sprenger
1

También encontré un problema al compilar módulos para varias opciones.

Lo que encontré:

Parece que no necesitamos agregarlo publishNonDefault trueal build.gradlearchivo lib , ya que Gradle 3.0.1 .

Después de descompilar una clase, BaseExtensionencontré esto:

public void setPublishNonDefault(boolean publishNonDefault) {
   this.logger.warn("publishNonDefault is deprecated and has no effect anymore. All variants are now published.");
}

Y en lugar de:

dependencies {
...
   Compile project(path: ':lib', configuration: 'config1Debug')
}

Deberíamos usar:

dependencies {
...
   implementation project(':lib')
}

Lo único importante es agregar una configurations {...}parte albuild.gradle .

Entonces, la variante final del build.gradlearchivo de la aplicación es:

buildTypes {
   debug {
      ...
   }

   release {
      ...
   }
}

flavorDimensions "productType", "serverType"
productFlavors {
   Free {
      dimension "productType"
      ...
   }
   Paid {
      dimension "productType"
      ...
   }
   Test {
      dimension "serverType"
      ...
   }
   Prod {
      dimension "serverType"
      ...
   }
}

configurations {
   FreeTestDebug
   FreeTestRelease
   FreeProdDebug
   FreeProdRelease
   PaidTestDebug
   PaidTestRelease
   PaidProdDebug
   PaidProdRelease
}

dependencies {
   implementation fileTree(dir: 'libs', include: ['*.jar'])
   implementation project(':lib')
   ...
}

Además, puede utilizar variantes de filtro para restringir variantes de compilación.

Ps no olvides incluir módulos en el settings.gradlearchivo, como:

include ':app'
include ':lib'
project(':lib').projectDir = new File('app/libs/lib')
Sergio
fuente
señor, ¿puede explicar cómo la secuencia de comandos determinará el clima para incluir la biblioteca en una determinada configuración o no? Quiero decir, tengo un caso en el que necesito usar algo de lib para un cierto sabor, pero no necesito usarlo para el otro sabor
Jenya Kirmiza
No me metí en tal situación. Pero un tutorial de Google developer.android.com/studio/build/dependencies recomienda agregar un prefijo antes del comando "implementación" en el bloque "dependencias {...}". Es decir, dependencias {proyecto de implementación pagada (': lib')}, o dependencias {proyecto de implementación de depuración (': lib')}, o cualquier combinación de variante múltiple dependencias {proyecto de implementación pagadaProdDebug (': lib')}. Compruébalo y danos tu opinión :)
Sergio