the studio script plug make for first configurar available added actualizar android gradle dalvik dynamic-class-loaders

script - gradle version android studio



Carga de clase personalizada en Dalvik con Gradle(sistema de compilaciĆ³n de Android) (3)

Según la introducción de Custom Class Loading en Dalvik por Fred Chung en el blog de desarrolladores de Android:

La máquina virtual de Dalvik proporciona instalaciones para que los desarrolladores realicen la carga de clases personalizada. En lugar de cargar archivos ejecutables ("dex") de Dalvik desde la ubicación predeterminada, una aplicación puede cargarlos desde ubicaciones alternativas como el almacenamiento interno o a través de la red.

Sin embargo, no muchos desarrolladores tienen la necesidad de realizar una carga de clases personalizada. Pero aquellos que lo hacen y siguen las instrucciones en esa publicación del blog, pueden tener algunos problemas para imitar el mismo comportamiento con Gradle, el nuevo sistema de compilación para Android introducido en Google I / O 2013.

¿Cómo se puede adaptar exactamente el nuevo sistema de compilación para realizar los mismos pasos intermedios que en el antiguo sistema de compilación (basado en Ant)?


El complemento Android Studio Gradle ahora ofrece soporte multidex nativo, que resuelve efectivamente el límite del método de 65k de Android sin tener que cargar manualmente las clases desde un archivo jar, y por lo tanto hace que el blog de Fred Chung quede obsoleto para ese propósito. Sin embargo, la carga de clases personalizadas desde un archivo jar en tiempo de ejecución en Android sigue siendo útil para fines de extensibilidad (por ejemplo, crear un marco de complemento para su aplicación ), por lo que abordaré el siguiente escenario de uso:

He creado un puerto de la aplicación de ejemplo original en el blog de Fred Chung para Android Studio en mi página de github aquí utilizando el complemento de la biblioteca de Android en lugar del complemento de Java. En lugar de intentar modificar el proceso de dex existente para dividirlo en dos módulos como en el blog, he puesto el código que queremos incluir en el archivo jar en su propio módulo, y agregué una tarea personalizada assembleExternalJar que dexes lo necesario archivos de clase después de que la tarea de assemble principal ha terminado.

Aquí está la parte relevante del archivo build.gradle para la biblioteca. Si el módulo de su biblioteca tiene dependencias que no están en el proyecto principal, probablemente necesitará modificar este script para agregarlos.

apply plugin: ''com.android.library'' // ... see github project for the full build.gradle file // Define some tasks which are used in the build process task copyClasses(type: Copy) { // Copy the assembled *.class files for only the current namespace into a new directory // get directory for current namespace (PLUGIN_NAMESPACE = ''com.example.toastlib'') def namespacePath = PLUGIN_NAMESPACE.replaceAll("//.","/") // set source and destination directories from "build/intermediates/classes/release/${namespacePath}/" into "build/intermediates/dex/${namespacePath}/" // exclude classes which don''t have a corresponding .java entry in the source directory def remExt = { name -> name.lastIndexOf(''.'').with {it != -1 ? name[0..<it] : name} } eachFile {details -> def thisFile = new File("${projectDir}/src/main/java/${namespacePath}/", remExt(details.name)+".java") if (!(thisFile.exists())) { details.exclude() } } } task assembleExternalJar << { // Get the location of the Android SDK ext.androidSdkDir = System.env.ANDROID_HOME if(androidSdkDir == null) { Properties localProps = new Properties() localProps.load(new FileInputStream(file(''local.properties''))) ext.androidSdkDir = localProps[''sdk.dir''] } // Make sure no existing jar file exists as this will cause dx to fail new File("${buildDir}/intermediates/dex/${PLUGIN_NAMESPACE}.jar").delete(); // Use command line dx utility to convert *.class files into classes.dex inside jar archive String cmdExt = Os.isFamily(Os.FAMILY_WINDOWS) ? ''.bat'' : '''' exec { commandLine "${androidSdkDir}/build-tools/${BUILD_TOOLS_VERSION}/dx${cmdExt}", ''--dex'', "--output=${buildDir}/intermediates/dex/${PLUGIN_NAMESPACE}.jar", "${buildDir}/intermediates/dex/" } copyJarToOutputs.execute() } task copyJarToOutputs(type: Copy) { // Copy the built jar archive to the outputs folder from ''build/intermediates/dex/'' into ''build/outputs/'' include ''*.jar'' } // Set the dependencies of the build tasks so that assembleExternalJar does a complete build copyClasses.dependsOn(assemble) assembleExternalJar.dependsOn(copyClasses)

Para obtener información más detallada, consulte el código fuente completo de la aplicación de ejemplo en mi github.


Mi equipo y yo recientemente alcanzamos las referencias de método de 64K en nuestra aplicación, que es el número máximo de archivos admitidos en un archivo dex. Para sortear esta limitación, necesitamos particionar parte del programa en varios archivos dex secundarios y cargarlos en tiempo de ejecución.

Seguimos la publicación del blog mencionada en la pregunta para el antiguo sistema de compilación basado en Ant y todo funcionaba bien. Pero recientemente sentimos la necesidad de pasar al nuevo sistema de compilación, basado en Gradle.

Esta respuesta no pretende reemplazar la publicación del blog completo con un ejemplo completo. En su lugar, simplemente le explicará cómo usar Gradle para modificar el proceso de construcción y lograr lo mismo. Tenga en cuenta que esta es probablemente una forma de hacerlo y cómo lo estamos haciendo actualmente en nuestro equipo. No significa necesariamente que sea la única manera.

Nuestro proyecto está un poco diferente y este ejemplo funciona como un proyecto Java individual que compilará todo el código fuente en archivos .class, los reunirá en un solo archivo .dex y, para finalizar, empaquetará ese archivo .dex en un .jar. expediente.

Empecemos...

En la raíz build.gradle tenemos el siguiente fragmento de código para definir algunos valores predeterminados:

ext.androidSdkDir = System.env.ANDROID_HOME if(androidSdkDir == null) { Properties localProps = new Properties() localProps.load(new FileInputStream(file(''local.properties''))) ext.androidSdkDir = localProps[''sdk.dir''] } ext.buildToolsVersion = ''18.0.1'' ext.compileSdkVersion = 18

Necesitamos el código anterior porque, aunque el ejemplo es un proyecto Java individual, todavía necesitamos usar componentes del SDK de Android. Y también necesitaremos algunas de las otras propiedades más adelante ... Entonces, en el build.gradle del proyecto principal, tenemos esta dependencia:

dependencies { compile files("${androidSdkDir}/platforms/android-${compileSdkVersion}/android.jar") }

También estamos simplificando los conjuntos de fuentes de este proyecto, que podrían no ser necesarios para su proyecto:

sourceSets { main { java.srcDirs = [''src''] } }

A continuación, cambiamos la configuración predeterminada de la tarea jar incorporada para simplemente incluir el archivo classes.dex en lugar de todos los archivos .class:

configure(jar) { include ''classes.dex'' }

Ahora necesitamos tener una nueva tarea que realmente ensamblará todos los archivos .class en un solo archivo .dex. En nuestro caso, también debemos incluir el JAR de la biblioteca Protobuf en el archivo .dex. Así que estoy incluyendo eso en el ejemplo aquí:

task dexClasses << { String protobufJarPath = '''' String cmdExt = Os.isFamily(Os.FAMILY_WINDOWS) ? ''.bat'' : '''' configurations.compile.files.find { if(it.name.startsWith(''protobuf-java'')) { protobufJarPath = it.path } } exec { commandLine "${androidSdkDir}/build-tools/${buildToolsVersion}/dx${cmdExt}", ''--dex'', "--output=${buildDir}/classes/main/classes.dex", "${buildDir}/classes/main", "${protobufJarPath}" } }

Además, asegúrese de tener la siguiente importación en algún lugar (normalmente en la parte superior, por supuesto) en su archivo build.gradle :

import org.apache.tools.ant.taskdefs.condition.Os

Ahora debemos hacer que la tarea jar dependa de nuestra tarea dexClasses , para asegurarnos de que nuestra tarea se ejecute antes de ensamblar el archivo .jar final. Lo hacemos con una simple línea de código:

jar.dependsOn(dexClasses)

Y hemos terminado ... Simplemente invoque a Gradle con la tarea de assemble habitual y su archivo .jar final, ${buildDir}/libs/${archivesBaseName}.jar contendrá un solo archivo classes.dex (además del MANIFEST.MF) expediente). Solo copie eso en la carpeta de activos de la aplicación (siempre puede automatizar eso con Gradle como lo hemos hecho pero eso está fuera del alcance de esta pregunta) y siga el resto de la publicación del blog.

Si tienes alguna duda, solo grita en los comentarios. Trataré de ayudar lo mejor que pueda.


Mira mi respuesta aquí . Los puntos clave son:

  • Use la propiedad additionalParameters dexCamelCase tareas dexCamelCase creadas dinámicamente para pasar --multi-dex a dx y crear múltiples archivos dex.
  • Use el cargador de clases multidex para usar los múltiples archivos dex.