Compartir código entre diferentes proyectos de Android Studio

A mi modo de ver, compartir código entre dos proyectos diferentes de Android Studio, es algo lo suficientemente laborioso y poco documentado como para merecer una entrada en el blog. Hay un poco de información en la página oficial de google, pero no me parece lo suficientemente clara.

Nuestro objetivo es tener dos (o más) proyectos diferentes de Android Studio, y tener una librería con código fuente que reutilizaremos  en todos ellos. Veamos cómo se hace.

Empezaremos por crear dos proyectos vacíos, en la misma carpeta. Para ello usaremos el asistente de Android Studio (Menu File-> New-> New Project). Los proyectos pueden ser todo lo complejos que queráis, pero para nuestro ejemplo, solo tendrán una actividad. Cuando hayamos terminado, tendremos en nuestra carpeta los dos proyectos tal que así:

Dos proyectos de Android Studio en la misma carpeta

Y en Android Studio, tendrán el siguiente aspecto. Es importante que estén en la misma carpeta, para simplificar el ejemplo.

Dos proyectos de Android Studio

Ahora supongamos que en el proyecto A, hemos creado una clase que queremos reutilizar en el proyecto B, la típica clase con funciones o métodos que son útiles para todos nuestros proyectos. Hemos creado una clase de ejemplo llamada Utilidades.java, que contiene un método de ejemplo, llamado “calcula”.

[crayon-5bf4c74e5c793597585056/]

La clase utilidades
La clase utilidades en nuestro modulo principal

 

Para compartir código entre proyectos, tendremos que crear un módulo “Android library”. Esto se hace en el menú File->New-> New Module->Android Library. Nosotros le hemos llamado sencillamente “libreria”.

Crear el modulo de libreria con el asistente de Android Studio

 

Después de pulsar “finish”, si observamos la vista de Proyecto de Android Studio, podremos ver que se ha añadido un módulo llamado librería al Proyecto A. Este módulo tiene la misma estructura que el modulo principal de la aplicación, y podreis observar que tiene su propio AndroidManifest.xml, su propio build.gradle, etc. Es importante ser consciente de esto, ya que si en la librería hacemos referencia a librerías de terceros, tendremos que especificarlas en el fichero build.gradle de la librería. Asimismo, si algun codigo fuente de nuestra librería necesita permisos especiales, tendremos que definirlos en el manifest de la misma.

Modulo de libreria añadido al proyecto
Modulo de libreria añadido al proyecto. La estructura es la misma que la de cualquier modulo.

 

El siguiente paso es mover la clase Utilidades, del módulo “app”, al modulo “libreria”. Podeis hacerlo de dos maneras: simplemente arrastrando la clase con el ratón, o usando la opción “refactorizar” del menú contextual.

Antes de mover la clase al modulo librería, la estructura de nuestro proyecto era la siguiente:

Antes de mover la clase utilidades al modulo libreria
Antes de mover la clase utilidades al modulo libreria

Y después de mover el la clase, la estructura es la siguiente. Obsérvese que la clase MainActivity esta marcada en rojo como errónea. Esto es porque en la clase, referenciamos a la clase Utilidades, pero esta ya no está disponible porque la hemos movido a otro módulo/paquete.

Despues de mover la clase utilidades al modulo libreria
Después de mover la clase utilidades al modulo libreria

 

Lo siguiente es configurar el proyecto de tal manera que incluya el módulo libreria. Esto se puede hacer dos maneras.

Opción 1: Menu File -> Project Structure. En esta ventana de configuración, podemos indicar las dependencias de nuestro proyecto. Seleccionaremos el módulo “app”, y en la pestaña de dependencias, indicaremos que necesitamos el módulo librería.

Configurando la dependencia con nuestra libreria
Configurando la dependencia con nuestra libreria

Opcion 2: mucho más fácil que usar el anterior menú, es incluir la dependencia directamente en el fichero build.gradle de nuestra app. El resultado final es el mismo, pero si lo hacemos manualmente, entenderemos mejor que es lo que hace Android Studio entre bastidores. Por lo tanto, al usar el menú anterior, lo que ha pasado es que se ha añadido una entrada al final del fichero build.gradle, indicando que se debe incluir al compilar el modulo llamado “libreria”.

[crayon-5bf4c74e5c7a9636048741/]

Ahora podemos corregir en nuestra MainActivity, el import de la clase Utilidades, y nuestro proyecto compilara correctamente. En el modulo “libreria”, podemos añadir todo lo que queramos: clases, actividades con sus respectivos layouts, recursos (drawables, pngs, ficheros de strings, etc), y todo ello puede ser referenciado en el módulo principal.

Incluir la librería en el proyecto B.

Hasta ahora hemos visto cómo crear una librería que pueda ser referenciada desde el módulo principal de nuestro proyecto de Android Studio. Por si solo, la utilidad de esto es reducida. Es cierto que podemos descargar el modulo principal de código, mover clases y recursos a la librería, y así mantenerlo todo un poco más ordenado. Esto es útil, pero más útil aún es poder incluir esa librería en otro proyecto diferente, nuestro proyecto B.

Lamentablemente, Android Studio no permite añadir dependencias a modulos que están en una carpeta diferente a la del proyecto. Pero si podemos configurarlo manualmente en los ficheros .gradle, concretamente en el fichero settings.gradle. Por lo tanto, en nuestro proyecto B, editaremos el fichero settings.gradle añadiendo lo siguiente:

1.- Proyecto B, settings.gradle

[crayon-5bf4c74e5c7ae705920176/]

La linea “include ‘:app'”, ya estaba presente, y hemos añadido dos líneas más en las que le indicamos al proyecto B, que incluya el módulo de libreria presente en la carpeta del Proyecto A. Si recompilais el proyecto, observareis que la librería se muestra en la vista de proyecto:

El modulo libreria, importado al proyecto B
El modulo libreria, incluido en el proyecto B.

 

2.- Incluir la librería en build.gradle

Al igual que hicimos en el proyecto A, tenemos que incluir la dependencia con nuestra librería en el fichero build.gradle, ya sea mediante los menús de Android Studio, o editando manualmente. Por lo tanto en el fichero build.gradle del módulo “app” del Proyecto B, incluiremos al final de nuestra dependencias la línea:

[crayon-5bf4c74e5c7b2970204764/]

Ya podemos referenciar en cualquier clase del Proyecto B, nuestra clase Utilidades, o cualquier recurso declarado en el módulo “libreria”. Asimismo, los cambios que hagamos en el modulo libreria, se verán reflejados automáticamente en el proyecto A, con todas las ventajas (desventajas) que esto tiene.

Bonus: sacar el módulo librería de la carpeta del Proyecto A.

Personalmente, esta estructura no nos convence demasiado, pues nos obliga a que el proyecto A siempre esté presente cuando trabajamos con el proyecto B. Esto es así, porque el modulo de librería, esta dentro del proyecto A. No parece muy elegante tener que depender siempre del Proyecto A, cuando trabajamos en el proyecto B. Esto nos obligaría por ejemplo, a tener que descargar los dos proyectos de nuestro sistema de control de versiones. Lo ideal seria poder tener el modulo librería en una carpeta independiente.

A día de hoy, no se puede lograr esto mediante los menus de configuracion de Android Studio, ya que cada vez que creamos un módulo nuevo mediante el asistente (File->New->New module), este se crea dentro del proyecto actual. Pero modificando un poco los ficheros settings.gradle, se puede conseguir.

Lo primero, es mover la carpeta “libreria” que contiene nuestro modulo, a otra localización. Para simplificar el ejemplo, la moveremos a la misma carpeta que contiene nuestros dos proyectos.

Esta es la carpeta que tenemos que mover.
Esta es la carpeta que tenemos que mover.

Cortamos y pegamos la carpeta, y la movemos a la carpeta que contiene nuestros proyectos:

Aqui debemos copiar la librería
Aquí debemos copiar la librería

 

Al hacer este movimiento, hemos roto nuestros proyectos, y ya no podemos compilarlos. Tenemos que retocar el fichero settings.gradle de ambos proyectos, para indicarle a donde hemos movido la librería, de la siguiente manera:

[crayon-5bf4c74e5c7b5183470728/]

Como podéis observar, simplemente hemos modificado la ruta, indicando a Android Studio que esa librería está en la carpeta inmediatamente superior, con respecto al fichero settings.gradle.

De esta manera podremos compartir código entre nuestras diferentes proyectos de android.

Consideraciones

¿Que pasa si defino el mismo recurso, por ejemplo, un color, o un string tanto en la app como en la librería?

Supongamos que en el fichero strings.xml del modulo librería, hemos definido una cadena de texto. Y supongamos también que en el fichero strings.xml del módulo app, hemos definido la misma cadena. ¿Cual de los dos se utiliza? La respuesta es, al compilar el proyecto, los recursos definidos en el módulo app tienen prioridad, por ello tenemos que tener cuidado, y si definimos cadenas o colores en el modulo librero, ser conscientes de que si tenemos el mismo recurso con el mismo nombre en el módulo principal, se utilizará el del módulo principal.

¿Que pasa con las dependencias de librerías de terceros, definidas en el fichero build.gradle?

Si en el modulo principal del proyecto, usamos una librería de terceros, ya sabemos que tenemos que incluirla en nuestras dependencias, en el fichero build.gradle. Si esa libreria es referenciada en el módulo librería, tenemos que incluir la dependencia en el fichero build.gradle del módulo de librería.

Las dependencias que añadamos en el fichero build.gradle de nuestra libreria, se incorporan automáticamente al módulo principal, por lo tanto no es necesario incluir las dependencias en ambos ficheros build.gradle.

Por lo tanto: si usas librerías de terceros, en ambos módulos, pon la dependencia en el build.gradle de la librería, y quitala del build.gradle del modulo principal.

Hay que tener cuidado con esto, ya que si definimos versiones diferentes de librerías en ambos ficheros, podemos crear una inconsistencia en nuestro proyecto.

¿Que pasa con los recursos declarados en el AndroidManifest.xml?

Pues lo mismo. Si en la librería usamos clases o llamadas a la apis que requieren permisos, tenemos que declarar los permisos en el AndroidManifest.xml de la librería. Lo mismo vale para Servicios, receivers, etc. Por ejemplo, si creamos un IntentService en la librería, declararemos en el AndroidManifest.xml de la librería el servicio, y podremos invocarlo desde el módulo principal de la app.