No meu artigo, quero falar sobre outro truque que pode ser facilmente implementado usando Gradle - reembalando pacotes de bibliotecas. Todo mundo que trabalhou um pouco com esse sistema de construção sabe que ele pode resolver automaticamente conflitos de diferentes versões de bibliotecas e, se desejar, você pode influenciar isso, por exemplo, para corrigir uma versão específica de uma biblioteca:
configurations.all { resolutionStrategy { force "org.ow2.asm:asm:7.2" } }
Infelizmente, isso nem sempre ajuda a resolver o problema do conflito de versão. Por exemplo, há um problema conhecido de que alguns dispositivos htc no firmware já possuem uma biblioteca gson e, se sua versão do gson for diferente da incorporada, poderão ocorrer problemas, pois o ClassLoader carregará apenas uma classe na memória e, nesse caso, será de sistema.
Esse problema também pode ocorrer ao desenvolver bibliotecas. Se você conectar 2 bibliotecas ao seu projeto que usam a mesma biblioteca de terceiros de versões diferentes, por exemplo 1 e 2, o Gradle resolverá e adotará a versão mais recente, a segunda. Mas se não houver compatibilidade com versões anteriores nessa biblioteca de terceiros e a segunda versão não puder ser usada apenas em vez da primeira, haverá problemas que certamente serão muito difíceis de rastrear pelo rastreamento. Uma biblioteca aguardando a primeira versão receberá as segundas aulas e apenas travará.
Encontrei um conflito de versão ao escrever um plug - in de graduação , ele usa a biblioteca asm , que conflitava. Depois de escrever o plug-in, verifiquei seu desempenho em um projeto de teste: está tudo bem, verifiquei em um projeto de estimação, está tudo bem também, mas quando o conectei a um projeto de trabalho real com várias dependências de terceiros, tive um problema.

A solução para o problema sob o corte.
No entanto, funcionou, o que deu errado?
Temos o rastreamento completo do erro:

Vemos que o erro no construtor da biblioteca asm ClassVisitor
está na linha 79. Vamos dar uma olhada lá, mas ao tentar abrir o ClassVisitor
, o estúdio ofereceu 2 opções

Meu plugin usa asm versão 7.2
, então vamos lá e na linha 79 vemos o seguinte:

Claramente não é disso que precisamos. Agora vá para o ClassVisitor
versão 6:

Apenas nossa IllegalArgumentException
sem uma mensagem. Meu plug-in usa a versão Opcodes.ASM7
api 7 do Opcodes.ASM7
e, na versão 6 da biblioteca, essa API ainda não existe, portanto, uma IllegalArgumentException
no construtor voa. Podemos concluir que o plugin recebe uma versão incorreta da biblioteca.
Pergunta de lixo, pensei, e fiz o seguinte:
configurations.all { resolutionStrategy { force "org.ow2.asm:asm:7.2" } }
Para meu pesar, isso não teve absolutamente nenhum efeito. Ainda não consegui descobrir o motivo exato pelo qual não é possível corrigir a versão asm excessivamente, embora o ./gradlew app:dependencies
mostre que a versão foi substituída pela 7.2. Se alguém tiver pensamentos ou suposições, ficarei feliz em ouvir uma opinião.
O problema deve ser resolvido de alguma forma
Uma série de pesquisas e aprofundamentos no trabalho do granizo começou. Como resultado, fui ao site da ASM, talvez eles saibam algo sobre isso. Acontece que eles realmente sabem, a resposta para a minha pergunta estava na seção FAQ. Eles dizem que, para substituir o pacote asm por outro, eles ainda oferecem um utilitário para isso. Ok, vamos tentar. Você só precisa conectar o plug-in e fazer uma pequena configuração:
apply plugin: 'org.anarres.jarjar' ... dependencies { implementation fileTree(dir: 'build/jarjar', include: ['*.jar']) implementation jarjar.repackage('asm') { from 'org.ow2.asm:asm:7.2' classRename "org.objectweb.asm.**", "stater.org.objectweb.asm.@1" } }
build/jarjar
nesse caso, o diretório no qual o arquivo jar da biblioteca asm com pacotes reembalados será gerado, portanto, é necessário abrir o acesso de dependência a esse diretório por meio do fileTree
. A biblioteca estará agora disponível com import stater.org.objectweb.asm.*
Em vez de org.objectweb.asm.*
. Este plugin possui várias configurações, mas no meu exemplo, basta alterar os pacotes.
Em seguida, percorra todo o projeto e altere a importação em todos os lugares, de org.objectweb.asm
para
stater.org.objectweb.asm
. Na minha opinião, um utilitário muito conveniente, muitas vezes mais fácil do que fazê-lo manualmente, especialmente ao atualizar a biblioteca, mudamos from 'org.ow2.asm:asm:7.2'
para a nova versão e o apelido do jar reembalado com a nova versão será gerado em máquina automática
Se você tiver apenas um projeto (não uma biblioteca), isso será suficiente para resolver conflitos insolúveis, como o gson mencionado no início do artigo. Mas se você, como eu, escreve uma biblioteca, isso não é tudo.
Resolvemos o problema de reembalagem, mas agora o asm
conectado ao projeto não através da dependência do repositório remoto do maven, mas através do arquivo jar local, que simplesmente será perdido quando a sua biblioteca for implantada e haverá um erro NoClassDefFoundError
. Mas esse problema é bastante simples de resolver:
Em nosso arquivo gradle, crie uma nova configuração:
configurations { extraLibs implementation.extendsFrom(extraLibs) }
Em seguida, mudamos
implementation fileTree(dir: 'build/jarjar', include: ['*.jar'])
em
extraLibs fileTree(dir: 'build/jarjar', include: ['*.jar'])
Redefinimos a tarefa responsável por coletar seu arquivo jar final e gravamos todas as bibliotecas com nossa nova configuração no apelido final do jar:
jar { from { configurations.extraLibs.collect { it.isDirectory() ? it : zipTree(it) } } }
Isso é tudo, implante nosso plug-in como antes, conecte-se ao projeto onde houve conflitos insolúveis e tudo funciona bem.
Tal reembalagem torna nossa biblioteca mais tolerante a falhas quando conectada a vários tipos de projetos com outras bibliotecas.
E se você apenas conectar o arquivo jar da biblioteca conflitante ao plug-in sem reembalar?
Má ideia, não levará a nada de bom. No processo de construção do projeto, há uma check...DuplicateClasses
tarefa tão interessante check...DuplicateClasses
, que simplesmente mata arquivos com os mesmos pacotes. Ou seja, arquivos obtidos do arquivo jar da biblioteca conectada e arquivos da mesma biblioteca conectados através do repositório remoto. O resultado será este erro:

Só isso. Obrigado a todos que leram!
Tulsa para reembalagem
Plug-in de exemplo