Aplicativo de vários sabores baseado na biblioteca de vários sabores no Android Gradle


102

Meu aplicativo tem vários sabores para diversos mercados de sistemas de faturamento no aplicativo.

Tenho uma única biblioteca que compartilha o código base de todos os meus projetos. Portanto, decidi adicionar esses sistemas de pagamento a esta biblioteca como variações de produtos.

A questão é: a biblioteca do Android pode ter variações de produtos?

Em caso afirmativo, como posso incluir diferentes sabores nos respectivos sabores do aplicativo?

Procurei muito e não consegui encontrar nada sobre esse cenário. A única coisa próxima que encontrei foi em http://tools.android.com/tech-docs/new-build-system/user-guide :

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

Mudei a configuração para coisas diferentes mas não funcionou!

Estou usando o Android Studio 0.8.2.


depois de muitas pesquisas, não encontrei maneira de conseguir isso, mesmo atualizando o plugin do Android para a última 3.4.2versão e o gradle para a última 5.5.1, ainda falhou com o tempo de compilação, ou a vinculação de recursos falhou no aapt, ou não consigo encontrar o símbolo dentro da biblioteca módulo
VinceStyling

Respostas:


141

Finalmente descobri como fazer isso, vou explicar aqui para outras pessoas que enfrentam o mesmo problema:

A parte principal é definir publishNonDefault como true na biblioteca build.gradle. Em seguida, você deve definir dependências conforme sugerido pelo guia do usuário.

Todo o projeto seria assim:

Biblioteca build.gradle:

apply plugin: 'com.android.library'

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

projeto build.gradle:

apply plugin: 'com.android.application'

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

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

Agora você pode selecionar a variação do aplicativo e o painel Build Variants e a biblioteca será selecionada de acordo e toda a construção e execução serão feitas com base na variação selecionada.

Se você tiver vários módulos de aplicativos baseados na biblioteca, o Android Studio reclamará sobre o conflito de seleção de variantes. Tudo bem, apenas ignore.

insira a descrição da imagem aqui


Obrigado por compartilhar, agora posso me livrar da minha solução alternativa defaultPublishConfig.
Delblanco

2
Executando AS 1.1.0, a solução acima parece ainda funcionar, no entanto 1) a escolha de builds de depurar / liberar foi perdida e eu continuo tendo problemas com AIDL encontrados na biblioteca que falham em produzir o código apropriado com muita frequência. Alguma opinião sobre isso?
3c71

1
@IgorGanapolsky buildTypes não tem nada a ver com isso. Cada tipo tem todos os tipos de construção (geralmente depuração e liberação) e todos eles funcionam com essa abordagem.
Ali,

1
@ An-droid define a biblioteca a ser usada para o sabor market1!
Ali,

1
Por que está definido para o tipo de compilação "release"? O tipo de compilação "release" é escolhido durante compilações de depuração?
WindRider

35

Há um problema com a resposta de Ali . Estamos perdendo uma dimensão muito importante em nossas variantes de construção. Se quisermos ter todas as opções (no meu exemplo abaixo de 4 (2 x 2)), só temos que adicionar configurações personalizadas no arquivo build.gradle do módulo principal para poder usar todos os multi-flavour multi-buildType no Build Variants. Também temos que definir publishNonDefault true no arquivo build.gradle do módulo de biblioteca .

Solução de exemplo:

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

}

Depois de fazer o mesmo em Minha aplicação Error:java.lang.RuntimeException: Error: more than one library with package name, occoured
Chetan Joshi

21

Atualização para Android Plugin 3.0.0 e superior

De acordo com a documentação oficial do Android - Migrar configurações de dependência para módulos locais ,

Com a resolução de dependência com reconhecimento de variante, você não precisa mais usar configurações específicas de variante, como freeDebugImplementation, para dependências de módulos locais - o plug-in cuida disso para você

Em vez disso, você deve configurar suas dependências da seguinte maneira:

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

Então, na resposta de Ali, mude

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

para

implementation project(':lib')

E o plugin cuidará das configurações específicas das variantes automaticamente. Espero que ajude outras pessoas que estão atualizando o plug-in do Android Studio para 3.0.0 e superior.


7

Meu plug-in do Android é 3.4.0 e acho que ele não precisa de configurações agora. Tudo o que você precisa é garantir que flavourDimensions e productFlavors no aplicativo contenham um productFlavor do mesmo flavourDimensions e productFlavors nas bibliotecas.Para amostra:

No build.gradle da minha biblioteca

apply plugin: 'com.android.library'

android {        
    ....
    flavorDimensions "mylibFlavor"

    productFlavors {
        market1
        market2
    }
}

build.gradle do aplicativo:

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

Após a sincronização, você pode alternar todas as opções na janela Build Variants: insira a descrição da imagem aqui


Mas e se eu não quiser ter os mesmos sabores no meu módulo de aplicativo principal? Suponha que eu tenha vários módulos de aplicativo que possuem seus próprios sabores específicos e um módulo comum que tem seus próprios sabores e desejo usar em meu aplicativo minha biblioteca com um sabor específico. Como você faria isso? Não faz sentido copiar meus sabores lib para todos os aplicativos.
Billda

@Billda Você não precisa copiar tudo, apenas mantenha um mesmo productFlavor no aplicativo, para minha amostra, posso manter market1 ou market2 no build.gradle do aplicativo.
JiajiaGu

2

Para fazer os sabores funcionarem em uma biblioteca AAR, você precisa definir defaultPublishConfig no arquivo build.gradle de seu módulo de biblioteca Android.

Para obter mais informações, consulte: Publicação da biblioteca .

Publicação da Biblioteca

Por padrão, uma biblioteca publica apenas sua versão de lançamento. Esta variante será usada por todos os projetos que fazem referência à biblioteca, não importando qual variante eles próprios construam. Esta é uma limitação temporária devido às limitações do Gradle que estamos trabalhando para remover. Você pode controlar qual variante é publicada:

android {defaultPublishConfig "debug"}

Observe que esse nome de configuração de publicação faz referência ao nome completo da variante. Liberar e depurar são aplicáveis ​​apenas quando não há sabores. Se você quisesse alterar a variante publicada padrão ao usar variações, você escreveria:

android {defaultPublishConfig "flav1Debug"}


1

No momento não é possível, embora se bem me lembro seja um recurso que eles querem adicionar. (Editar 2: link , link2 )

Editar: no momento, estou usando a defaultPublishConfigopção de declarar qual variante da biblioteca foi publicada:

android {
    defaultPublishConfig fullRelease
    defaultPublishConfig demoRelease 
}

1
Portanto, cada vez que vou compilar o aplicativo, devo alterar isso no build.gradle da biblioteca?
Ali

Bem, sim ... cada vez que você quiser compilar o aplicativo com um sabor diferente.
Delblanco

Na verdade, quando eu defino sabores para o módulo de biblioteca, o pacote R herdado não é encontrado no módulo app.
Ali

Você sincronizou os arquivos do Gradle no AS?
Delblanco

@Delblanco Isso parece um trabalho manual e muito frágil (os desenvolvedores são preguiçosos e se esquecem de modificar seus arquivos build.gradle).
IgorGanapolsky,

1

Eu sei que este assunto foi encerrado, mas apenas uma atualização com o gradle 3.0, veja isto: https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html#variant_aware e grep matchingFallbackse missingDimensionStrategy. Agora é muito mais simples declarar as dependências entre os tipos de módulo.

... e neste caso preciso com gradle3.0, como os sabores compartilham o mesmo nome, o gradle os mapeia magicamente, não há configuração necessária.


Para mim, parece que esse material gerado pelo tempo de execução foi ignorado. Por exemplo, simonvt-> geração de esquemas não está mais funcionando com a nova forma para mim. : - /
Stefan Sprenger

1

Também tive um problema ao compilar módulos para várias opções.

O que eu encontrei:

Parece que não precisamos adicionar publishNonDefault trueao build.gradlearquivo lib , desde o Gradle 3.0.1 .

Depois de descompilar uma classe, BaseExtensiondescobri o seguinte:

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

E em vez de:

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

Devemos usar:

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

O importante é adicionar uma configurations {...}parte ao build.gradle.

Portanto, a variante final do build.gradlearquivo do aplicativo é:

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

Além disso, você pode usar as variantes de filtro para restringir as variantes de compilação.

PS não se esqueça de incluir módulos no settings.gradlearquivo, como:

include ':app'
include ':lib'
project(':lib').projectDir = new File('app/libs/lib')

senhor, você pode explicar como o script determinará o tempo para incluir a biblioteca em uma determinada configuração ou não? Quer dizer, tenho um caso em que preciso usar alguma lib para um determinado sabor, mas não preciso usá-la para outro sabor
Jenya Kirmiza,

Não entrei em tal situação. Mas um tutorial do google developer.android.com/studio/build/dependencies recomenda adicionar um prefixo antes do comando "implementação" no bloco "dependências {...}". Ou seja, dependências {paidImplementation project (': lib')}, ou dependências {debugImplementation project (': lib')}, ou quaisquer dependências de combinação de múltiplas variantes {paidProdDebugImplementation project (': lib')}. Confira e nos dê um feedback :)
Sergio
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.