lunes, 28 de julio de 2014

Maven en Android

Navegando en la web, encontrè un gran trabajo de la gente de "Adictos al Trabajo", y como una linda forma de recomendarlos, vamos a repasar esta importante herramienta llamada Maven, y como la utilizamos en el desarollo Android, tomando como base el documento creado por ellos.
Fuente de los ejemplos, codigo y texto:

Trabajaremos con un plugin llamado "maven-android-plugin" que nos permite crear proyectos Maven específicos para Android. Toda la información relativa a este plugin la podéis encontrar aquí.
Nos proporciona comandos específicos de Maven para lanzar el emulador, desplegar la aplicación en todos los dispositivos conectados, arrancar la aplicación y eliminar la aplicación; y sobre todo poder integrar el proyecto fácilmente en nuestro proceso de integración continua.

Creación del proyecto

Lo primero que vamos a hacer es crear el proyecto Maven con el arquetipo de Android que nos ofrecen. Para ello abrimos un terminal, nos situamos en el directorio donde queramos crear el proyecto y ejecutamos:
mvn archetype:generate -DarchetypeArtifactId=android-quickstart -DarchetypeGroupId=de.akquinet.android.archetypes     
-DarchetypeVersion=1.0.8 -DgroupId=com.autentia -DartifactId=calc-android -Dversion=1.0-SNAPSHOT  

Para ilustrar el ejemplo vamos a hacer una calculadora. Ahora abrimos Eclipse e importamos el proyecto como proyecto de Maven. Si el Eclipse está bien configurado debería detectar el proyecto como un proyecto gestionado por Maven de Android. Para saberlo basta con que pinchemos con el botón derecho sobre la raíz del proyecto y ver que en la sección "Run As" aparece la opción "Android Application".
Si abrimos el fichero pom.xml que nos ha generado, vemos que a parte de las dependencias necesarias, ya tenemos declarado el plugin "maven-android-plugin" con una configuración por defecto.
<plugin>  
  1.    <groupid>com.jayway.maven.plugins.android.generation2</groupid>  
  2.    <artifactid>android-maven-plugin</artifactid>  
  3.    <version>3.1.1</version>  
  4.    <configuration>  
  5.        <androidmanifestfile>${project.basedir}/AndroidManifest.xml</androidmanifestfile>  
  6.        <assetsdirectory>${project.basedir}/assets</assetsdirectory>  
  7.        <resourcedirectory>${project.basedir}/res</resourcedirectory>  
  8.        <nativelibrariesdirectory>${project.basedir}/src/main/native</nativelibrariesdirectory>  
  9.        <sdk>  
  10.            <platform>10</platform>  
  11.        </sdk>  
  12.        <undeploybeforedeploy>true</undeploybeforedeploy>  
  13.    </configuration>  
  14.    <extensions>true</extensions>  
  15. </plugin>  
La configuración viene por defecto para la plataforma 10 de Android que se corresponde con la versión 2.3.3 de Android. Por lo que nos vamos a asegurar de tener un emulador compatible creado en el SDK. En caso contrario, lo podemos hacer desde Eclipse, simplemente abriendo el "Android Virtual Device (AVD) Manager" desde la opción Window del menú de Eclipse.
Para crear una nueva instancia pulsamos en "New", establecemos un nombre para el dispositivo (AVD-2.3.3-10), seleccionamos una plataforma objetivo, en este caso Api Level 10 y el resto de opciones las dejamos por defecto.
Para establecer este dispositivo como predeterminado a la hora de lanzar el emulador con el plugin de Maven tenemos que dejar la configuración de esta forma:
  1. <plugin>  
  2.    <groupid>com.jayway.maven.plugins.android.generation2</groupid>  
  3.    <artifactid>android-maven-plugin</artifactid>  
  4.    <version>3.1.1</version>  
  5.    <configuration>  
  6.        <androidmanifestfile>${project.basedir}/AndroidManifest.xml</androidmanifestfile>  
  7.        <assetsdirectory>${project.basedir}/assets</assetsdirectory>  
  8.        <resourcedirectory>${project.basedir}/res</resourcedirectory>  
  9.        <nativelibrariesdirectory>${project.basedir}/src/main/native</nativelibrariesdirectory>  
  10.        <sdk>  
  11.            <platform>10</platform>  
  12.        </sdk>  
  13.        <undeploybeforedeploy>true</undeploybeforedeploy>  
  14.        <emulator>  
  15.            <avd>AVD-2.3.3-10</avd>  
  16.        </emulator>  
  17.    </configuration>  
  18.    <extensions>true</extensions>  
  19. </plugin>  

Creamos la lógica de negocio

En este caso simple, la lógica de negocio va a consistir en una calculadora que contenga los típicos métodos de suma, resta, multiplicación y división. Como no puede ser de otra forma la implementación de la lógica de negocio la comenzados con un test el cual no irá descubriendo el diseño de nuestra solución. Para ello necesitamos incluir la dependencia JUnit en el pom.xml del proyecto. Después de todos los ciclos de TDD necesarios, este es el código del test resultante.
  1. package com.autentia.logic;  
  2.  
  3. import org.junit.Assert;  
  4. import org.junit.Test;  
  5.  
  6. public class CalculadoraTest {  
  7.      
  8.    @Test  
  9.    public void addTwoOperands(){  
  10.        Double op1 = Double.valueOf(3.0);  
  11.        Double op2 = Double.valueOf(4.0);  
  12.          
  13.        Assert.assertEquals(Double.valueOf(7.0), Calculadora.add(op1, op2));  
  14.          
  15.    }  
  16.      
  17.    @Test  
  18.    public void sustractTwoOperands(){  
  19.        Double op1 = Double.valueOf(5.0);  
  20.        Double op2 = Double.valueOf(2.0);  
  21.          
  22.        Assert.assertEquals(Double.valueOf(3.0), Calculadora.sustract(op1, op2));  
  23.    }  
  24.      
  25.    @Test  
  26.    public void multiplyTwoOperands(){  
  27.        Double op1 = Double.valueOf(6.0);  
  28.        Double op2 = Double.valueOf(2.0);  
  29.          
  30.        Assert.assertEquals(Double.valueOf(12.0), Calculadora.multiply(op1, op2));  
  31.    }  
  32.      
  33.    @Test  
  34.    public void divideTwoOperands(){  
  35.        Double op1 = Double.valueOf(10.0);  
  36.        Double op2 = Double.valueOf(2.0);  
  37.          
  38.        Assert.assertEquals(Double.valueOf(5.0), Calculadora.divide(op1, op2));  
  39.    }  
  40. }  
Y el código resultante para la clase Calculadora
  1. package com.autentia.logic;  
  2.  
  3. public class Calculadora {  
  4.  
  5.    public static Double add(Double op1, Double op2) {  
  6.        return op1 + op2;  
  7.    }  
  8.  
  9.    public static Double sustract(Double op1, Double op2) {  
  10.        return op1 - op2;  
  11.    }  
  12.      
  13.    public static Double multiply(Double op1, Double op2) {  
  14.        return op1 * op2;  
  15.    }  
  16.      
  17.    public static Double divide(Double op1, Double op2) {  
  18.        return op1 / op2;  
  19.    }  
  20. }  

5. Definición de la interfaz de usuario

Ahora vamos a crear la pantalla que permita al usuario introducir los dos operandos y seleccionar la operación a realizar. Para ello, editamos el fichero res/layout/main.xml que nos ha creado el plugin con un contenido por defecto.
Utilizando el "Graphical Layout" de Eclipse creamos visualmente la interfaz deseada que se podría parecer a la que se muestra a continuación.
La interfaz es claramente mejorable pero para el propósito de este tutorial es suficiente. Tenemos que tener presente los ids que le damos a los distintos elementos, en mi caso:
  • Campo de texto 'Operando 1': strOp1
  • Campo de texto 'Operando 2': strOp2
  • Botón de sumar: btnAdd
  • Botón de restar: btnSustract
  • Botón de multiplicar: btnMultiply
  • Botón de dividir: btnDivide
  • Etiqueta de 'Resultado': txtResult
Tendremos que tener en cuenta estos ids a la hora de interactuar con el interfaz gráfico desde la actividad.

6. Creamos la actividad

Toda aplicación de Android necesita al menos de una actividad; por lo que nosotros vamos a renombrar la actividad que el plugin te crea por defecto 'HelloAndroidActivity' por 'CaclActivity'. No olvidéis cambiar el nombre también en la declaración de la actividad dentro del fichero AndroidManifest.xml.
Ahora vamos a incluir los manejadores de los eventos cuando pulsamos algún botón de operación y un manejador para borrar el resultado cuando se pulse encima del campo de texto. El código resultante de la actividad sería:
  1. package com.autentia;  
  2.  
  3. import com.autentia.logic.Calculadora;  
  4.  
  5. import android.app.Activity;  
  6. import android.os.Bundle;  
  7. import android.util.Log;  
  8. import android.view.View;  
  9. import android.widget.Button;  
  10. import android.widget.EditText;  
  11. import android.widget.TextView;  
  12.  
  13. public class CalcActivity extends Activity {  
  14.  
  15.    private static String TAG = "calc-android";  
  16.  
  17.    @Override  
  18.    public void onCreate(Bundle savedInstanceState) {  
  19.        super.onCreate(savedInstanceState);  
  20.        Log.i(TAG, "onCreate");  
  21.        setContentView(R.layout.main);  
  22.          
  23.        //Cuando el usuario pulsa el campo de texto Resultado se borra el contenido  
  24.        final TextView txtResult = (TextView) findViewById(R.id.txtResult);  
  25.        txtResult.setOnClickListener(new EditText.OnClickListener() {  
  26.            public void onClick(View v) {  
  27.                txtResult.setText("");  
  28.            }  
  29.        });  
  30.  
  31.        //Cuando el usuario pulsa el botón Add se realiza la suma de los operandos  
  32.        final Button btnAdd = (Button) findViewById(R.id.btnAdd);  
  33.        btnAdd.setOnClickListener(new Button.OnClickListener() {  
  34.            public void onClick(View v) {  
  35.                Double[] operands = getOperandsInView();  
  36.                txtResult.setText(String.valueOf(Calculadora.add(operands[0], operands[1])));  
  37.            }  
  38.        });  
  39.          
  40.        //Cuando el usuario pulsa el botón Sustract se realiza la resta de los operandos  
  41.        final Button btnSustract = (Button) findViewById(R.id.btnSustract);  
  42.        btnSustract.setOnClickListener(new Button.OnClickListener() {  
  43.            public void onClick(View v) {  
  44.                Double[] operands = getOperandsInView();  
  45.                txtResult.setText(String.valueOf(Calculadora.sustract(operands[0], operands[1])));  
  46.            }  
  47.        });  
  48.          
  49.        //Cuando el usuario pulsa el botón Multiply se realiza la multiplicación de los operandos  
  50.        final Button btnMultiply = (Button) findViewById(R.id.btnMultiply);  
  51.        btnMultiply.setOnClickListener(new Button.OnClickListener() {  
  52.            public void onClick(View v) {  
  53.                Double[] operands = getOperandsInView();  
  54.                txtResult.setText(String.valueOf(Calculadora.multiply(operands[0], operands[1])));  
  55.            }  
  56.        });  
  57.          
  58.        //Cuando el usuario pulsa el botón Sustract se realiza la resta de los operandos  
  59.        final Button btnDivide = (Button) findViewById(R.id.btnDivide);  
  60.        btnDivide.setOnClickListener(new Button.OnClickListener() {  
  61.            public void onClick(View v) {  
  62.                Double[] operands = getOperandsInView();  
  63.                txtResult.setText(String.valueOf(Calculadora.divide(operands[0], operands[1])));  
  64.            }  
  65.        });  
  66.          
  67.    }  
  68.      
  69.    private Double[] getOperandsInView(){  
  70.        final String strOp1 = ((EditText) findViewById(R.id.strOp1)).getText().toString();  
  71.        final String strOp2 = ((EditText) findViewById(R.id.strOp2)).getText().toString();  
  72.  
  73.        Double op1 = Double.valueOf(0);  
  74.        if (!"".equals(strOp1)){  
  75.            op1 = Double.valueOf(strOp1);  
  76.        }  
  77.          
  78.        Double op2 = Double.valueOf(0);  
  79.        if (!"".equals(strOp2)){  
  80.            op2 = Double.valueOf(strOp2);  
  81.        }  
  82.          
  83.        Double[] operands =  new Double[2];  
  84.        operands[0] = op1;  
  85.        operands[1] = op2;  
  86.          
  87.        return operands;  
  88.          
  89.    }  
Ejecutamos un 'mvn clean install' del proyecto para ver que todo es correcto y para que nos genere el .apk que tenemos que instalar en el dispositivo móvil.

7. Probando la aplicación

Para probar la aplicación vamos a lanzar el emulador que creamos anteriormente. Para ello simplemente abrimos un terminal, nos situamos en el directorio raíz del proyecto y ejecutamos 'mvn android:emulator-start'. Pasados unos segundos el sistema nos mostrará el emulador con el que podremos interactuar como si de un móvil se tratase.
Ahora vamos a instalar el .apk del proyecto en el emulador o cualquier dispositivo que tengamos conectado. Para ello desde el terminal ejecutamos 'mvn android:deploy'. Una vez finalizado si accedemos a las aplicaciones del móvil debemos ver la nuestra. Ahora podemos ejecutar la aplicación directamente en el emulador o ejecutar desde línea de comandos 'mvn android:run' para poder interactuar con la aplicación.
Para eliminar la aplicación del dispositivo basta con ejecutar 'mvn android:undeploy'. En caso de querer actualizar alguna modificación en la aplicación podríamos ejecutar 'mvn android:redeploy' sin olvidar antes volver a ejecutar 'mvn clean install'.

Conclusiones


Maven es una importante herramienta de productividad que se utiliza en muchas empresas, es por eso que en este workshop presentamos un “HolaMundo” que nos permita saber como configurarlo en un proyecto.