En mi artículo, quiero hablar sobre otro truco que puede implementarse con bastante facilidad utilizando Gradle: reempaquetar paquetes de biblioteca. Todos los que incluso han trabajado un poco con este sistema de compilación saben que automáticamente sabe cómo resolver conflictos de diferentes versiones de bibliotecas, y si lo desea, puede influir en esto, por ejemplo, sobreescribir una versión específica de una biblioteca:
configurations.all { resolutionStrategy { force "org.ow2.asm:asm:7.2" } }
Desafortunadamente, esto no siempre ayuda a resolver el problema del conflicto de versiones. Por ejemplo, existe un problema conocido de que algunos dispositivos htc en el firmware ya tienen una biblioteca gson, y si su versión de gson difiere de la versión incorporada, pueden surgir problemas, ya que ClassLoader cargará solo una clase en la memoria, y en este caso será una de sistema.
Este problema también puede ocurrir al desarrollar bibliotecas. Si conecta 2 bibliotecas a su proyecto que usan la misma biblioteca de terceros de diferentes versiones, por ejemplo 1 y 2, entonces Gradle resolverá y tomará la versión más nueva, la segunda. Pero si no hay compatibilidad con versiones anteriores en esta biblioteca de terceros y la segunda versión no se puede usar en lugar de la primera, entonces habrá problemas que seguramente serán muy difíciles de rastrear. Una biblioteca en espera de la primera versión recibirá las segundas clases y simplemente se bloqueará.
Encontré un conflicto de versión al escribir un complemento de graduación , usa la biblioteca asm , lo que está en conflicto. Después de escribir el complemento, verifiqué su rendimiento en un proyecto de prueba: todo está bien, revisé un proyecto favorito, también todo está bien, pero cuando lo conecté a un proyecto de trabajo real con un montón de dependencias de terceros, me encontré con un problema.

La solución al problema debajo del corte.
Sin embargo, funcionó, ¿qué salió mal?
Obtenemos el seguimiento de error completo:

Vemos que el error en el constructor de la biblioteca asm ClassVisitor
está en la línea 79. Miremos allí, pero al intentar abrir ClassVisitor
, el estudio ofreció 2 opciones

Mi complemento usa la versión 7.2
asm, así que vamos allí y en la línea 79 vemos lo siguiente:

Claramente, esto no es lo que necesitamos. Ahora ve a ClassVisitor
versión 6:

Solo nuestra IllegalArgumentException
sin mensaje. Mi complemento utiliza la versión ASM api 7 de Opcodes.ASM7
, y en la versión 6 de la biblioteca esta api aún no existe, por lo tanto, una IllegalArgumentException
en el constructor vuela. Podemos concluir que el complemento recibe una versión incorrecta de la biblioteca.
Pregunta basura, pensé, e hice esto:
configurations.all { resolutionStrategy { force "org.ow2.asm:asm:7.2" } }
Para mi pesar, esto no tuvo absolutamente ningún efecto. Todavía no pude encontrar la razón exacta por la cual no es posible sobreescribir la versión asm, aunque el ./gradlew app:dependencies
muestra que la versión ha sido reemplazada por 7.2. Si alguien tiene pensamientos o suposiciones, estaré encantado de escuchar una opinión.
El problema debe resolverse de alguna manera
Comenzó una serie de búsqueda en Google y profundización en el trabajo del granizo. Como resultado, fui al sitio web de ASM, tal vez ellos saben algo sobre esto. Resultó que realmente lo saben, la respuesta a mi pregunta estaba en la sección de preguntas frecuentes. Dicen que para reemplazar el paquete asm con otro, incluso ofrecen una utilidad para esto. Ok, vamos a intentarlo Solo necesita conectar el complemento y hacer una pequeña configuración:
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
en este caso, el directorio en el que se generará el archivo jar de la biblioteca asm con paquetes reempaquetados, por lo que debe abrir el acceso de dependencia a este directorio a través de fileTree
. La biblioteca ahora estará disponible con import stater.org.objectweb.asm.*
En lugar de org.objectweb.asm.*
. Este complemento tiene varias configuraciones, pero en mi ejemplo, solo cambiar los paquetes fue suficiente.
A continuación, org.objectweb.asm
todo el proyecto y cambie la importación en todas partes desde org.objectweb.asm
a
stater.org.objectweb.asm
. En mi opinión, una utilidad muy conveniente, muchas veces más fácil que hacerlo a mano, especialmente al actualizar la biblioteca, simplemente cambiamos from 'org.ow2.asm:asm:7.2'
a la nueva versión y se generará el apodo de jar reempaquetado con la nueva versión en máquina automática
Si solo tiene un proyecto (no una biblioteca), esto será suficiente para resolver conflictos insolubles, como el mencionado por Gson al principio del artículo. Pero si usted, como yo, escribe una biblioteca, eso no es todo.
Resolvimos el problema de reempaquetado, pero ahora asm
conectado al proyecto no a través de la dependencia del repositorio remoto de Maven, sino a través del archivo jar local, que simplemente se perderá cuando se implemente su biblioteca y habrá un error de NoClassDefFoundError
. Pero este problema es bastante simple de resolver:
En nuestro archivo gradle, cree una nueva configuración:
configurations { extraLibs implementation.extendsFrom(extraLibs) }
Luego cambiamos
implementation fileTree(dir: 'build/jarjar', include: ['*.jar'])
en
extraLibs fileTree(dir: 'build/jarjar', include: ['*.jar'])
Redefinimos la tarea responsable de recopilar su archivo jar final y escribimos todas las bibliotecas con nuestra nueva configuración en el apodo final del jar:
jar { from { configurations.extraLibs.collect { it.isDirectory() ? it : zipTree(it) } } }
Eso es todo, implemente nuestro complemento como antes, conéctese al proyecto donde hubo conflictos irresolubles y todo funciona bien.
Tal reempaquetado hace que nuestra biblioteca sea más tolerante a fallas cuando se conecta a varios tipos de proyectos con otras bibliotecas.
¿Y si solo conecta el archivo jar de la biblioteca en conflicto al complemento sin volver a empaquetar?
Mala idea, no conducirá a nada bueno. En el proceso de construcción del proyecto, hay una check...DuplicateClasses
tareas tan interesante check...DuplicateClasses
, que simplemente matará los archivos con los mismos paquetes. Es decir, archivos obtenidos del archivo jar de la biblioteca conectada y archivos de la misma biblioteca conectados a través del repositorio remoto. El resultado será este error:

Eso es todo. ¡Gracias a todos los que leyeron!
Tulsa para reempacar
Complemento de ejemplo