Organizando dependencias con Gradle

Buenas a todos!

En un proyecto Android, Gradle es el encargado de las tareas de compilación, empaquetado y generación de apks, entre otras tareas. Hoy quiero hablar sobre una de estas tareas: la gestión dependencias de terceros.

Gradle permite añadir dependencias a nuestro proyecto de una forma tan fácil como añadir una línea al fichero build.gradle del módulo app. Algo parecido a esto.

Esto tiene un problema cuando la biblioteca en cuestión está dividida en varias dependencias, y tienes que cambiar la versión de cada una de ellas. Si se declaran así las dependencias tendremos que actualizar la versión tantas veces como la uses en cada uno de los módulos de tu aplicación.

Para agilizar la actualización de versiones y para ordenar y limpiar los ficheros gradle de mi aplicación investigué como podía definir la versión de la biblioteca aparte en un solo lugar y así cuando haya que actualizar la versión, cambiarlo sólo ahí. Encontré en el repo Android-CleanArchitecture de Fernando Cejas una forma que me gustó.

Las dependencias de todo el proyecto se definen en un nuevo fichero, dependencies.gradle. En él se definen las versiones de cada biblioteca y además el paquete asociado a la biblioteca. Este fichero tiene esta pinta:

Las depedencias del módulo app quedan muy limpias, ya que además de llevarnos las versiones a otro fichero, también movimos el nombre del paquete, el SDK mínimo, el código de versión y el nombre de la versión.

Ahora ya no tenemos cambiar las versiones en muchos sitios, sino solo en uno. Otra ventaja que me parece interesante es que este fichero de dependencias aparte podría ser compartido para el resto de apps que desarrolles. Es decir, normalmente las dependencias entre las aplicaciones no suelen variar, por lo que podría copiarse cambiando el nombre del paquete y la versión y todo seguiría funcionando 🙂

Un saludo!

Kotlin interfaces mutantes

Buenas!

Hace ya casi un año que publiqué mi aplicación TopProfe para votar a los profesores de mi universidad y últimamente he decidido que es un buen momento para refactorizarlo y además, migrarlo a Kotlin.

Tras leerme el libro de @lime_cl, decidí ponerme manos a la obra y migrar la aplicación entera a Kotlin. Poco a poco iré escribiendo posts sobre las cosas chulas que he cambiado, y como ha mejorado el código con la introducción de Kotlin.

La primera feature de la que os quiero hablar es la posibilidad que Kotlin brinda para poder implementar métodos en las interfaces. También se pueden definir variables que más tarde sobreescribirá el que implemente dicha interfaz.

Imaginad la aplicación de TopProfe, que tiene varios listados de profesores, la interfaz que implementa la vista sería algo así en Java:

Los típicos métodos para añadir el listado de profesores al adapter correspondiente, y para navegar hasta el detalle de un profesor al hacer click sobre él. Todos los Fragments/Activities que implementen esta interfaz tendrán código repetido que es costoso mantener.

La solución que he adoptado en Kotlin para no repetir código, consiste en usar lo que he bautizado como ‘interfaces mutantes’ introduciendo en ellas código compartido entre todos los listados de profesores.

De esta forma, las vistas que tengan un listado de profesores tan solo tendrán que sobreescribir las dos variables de la interfaz, el Adapter y el Context necesario, y no tendrán código repetido. El adapter es uno genérico que me permite no tener que escribir un adapter para cada tipo de datos que quiera mostrar. En el siguiente post hablaré sobre cómo está implementado :).

En el caso de los listados de las asignaturas, la interfaz mutante queda de la siguiente manera. Si os dais cuenta, se vuelve a repetir código, y si el objetivo de esto es no hacerlo… ¿estoy ganando algo?

Cuando me di cuenta de que no era la solución más óptima, decidí ir un poco más allá. Como el Adapter está preparado para recibir genéricos, podría separar aún más las operaciones que hay que hacer para añadir elementos al Adapter. La solución es esta:

Así el código para añadir elementos al Adapter se escribe una sola vez, y la interfaces anteriores quedan de esta manera:

De momento este es el primer post de mis experiencias con Kotlin, pero pronto iré publicando más cositas :)!

Nos vemos!

¡Soy español en Android!

Hola a todos!

Como os comenté en el post anterior, en septiembre empecé un Máster de Ingeniería Informática por la UNED. Cuando ví las asignaturas, se me iluminaron los ojos al leer ‘Sistemas operativos de dispositivos móviles’. Mi gozo en un pozo al ver que era optativa, pero bueno, oye, que la iba a cursar estaba seguro.

La asignatura tiene nueve temas, en los que cinco de ellos son de Android y el resto de iOS. El temario sobre ambos sistemas operativos está muy bien estructurado, y explicado, entra en detalles que como desarrollador de aplicaciones no conoces.

En el caso de Android explica en detalle cada uno de los ficheros necesarios para el boot y los pasos necesarios en el arranque. Entendí por fin quién o qué es zygote, y como funciona. Son transparencias realmente recomendables.

La primera práctica de la asignatura fue arrancar un emulador y meterse en la shell del dispositivo. Una vez ahí podías ver los ficheros de los que se hablaba en la teoría, y como con todo en la vida, es interesante ver que la teoría se puede aplicar a la práctica.

La segunda práctica consiste en hacer una aplicación. Esta aplicación consta de un cuadro de texto en el que el usuario introduce su nombre, y seguidamente, aparece en pantalla un saludo personalizado con su nombre. A medida que avanzas la práctica se complica. Se van añadiendo funcionalidades como guardar el nombre del usuario en disco para cuando arranque la aplicación, aparezca el saludo ya sin que se vuelva a introducir su nombre.

Una práctica introductoria, pero interesante que te explica lo que es el framework de Android y como funciona. También te familiarizas con el uso de Android Studio y el emulador si no lo habías usado antes.

Hasta aquí, todo perfecto e interesante. Pero me gustaría enseñaros el código final de la práctica siguiendo las indicaciones de la memoria.

public class ActividadPrincipal extends AppCompatActivity {

    public final static String SALUDO = "com.uned.dia.saludar.SALUDO";
    public final static String NOMBRE_GUARDADO_EN_DISCO = "com.uned.dia.saludar.NOMBRE_GUARDADO_EN_DISCO";
    public final static String SALUDO_GUARDADO = "com.uned.dia.saludar.SALUDO_GUARDADO";
    public static final String DESPEDIDA = "com.uned.dia.saludar.DESPEDIDA";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.vista_actividad_principal);

        SharedPreferences preferencias = this.getPreferences(Context.MODE_PRIVATE);
        String nombreGuardado = preferencias.getString(NOMBRE_GUARDADO_EN_DISCO, "");
        escribirNombre(nombreGuardado);
    }

    public void Despedida(View view) {
        String textoDespedida = crearDespedida(obtenerNombre());
        escribirSalida("");
        Intent intent = new Intent(this, Despedida.class);
        intent.putExtra(DESPEDIDA, textoDespedida);
        startActivity(intent);
    }

    private String crearDespedida(String s) {
        return String.format(Locale.getDefault(), getString(R.string.despedida), s);
    }

    public void GuardarNombre(View view) {
        String nombre = obtenerNombre();
        SharedPreferences preferencias = this.getPreferences(Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = preferencias.edit();
        editor.putString(NOMBRE_GUARDADO_EN_DISCO, nombre);
        editor.commit();
        escribirNombre(getString(R.string.nombre_guardado_OK));
    }

    public void SaludarOtraApp(View view) {
        String textoSaludo = crearSaludo(obtenerNombre());
        escribirSalida("");
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_SEND);
        intent.putExtra(Intent.EXTRA_TEXT, textoSaludo);
        intent.setType("text/plain");
        PackageManager packageManager = getPackageManager();
        List activities= packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        if(activities.size() > 0){ intent.putExtra(Intent.EXTRA_TEXT, textoSaludo);
            startActivity(intent);
        }
        else {
            escribirSalida( getString(R.string.error_lanzando_app));
        }
    }

    public void SaludarActividadNueva(View view) {
        String textoSaludo = crearSaludo(obtenerNombre());
        escribirSalida("");
        Intent intent = new Intent(this, MostrarSaludo.class);
        intent.putExtra(SALUDO, textoSaludo);
        startActivity(intent);
    }

    public String obtenerNombre() {
        EditText editarTexto = (EditText) findViewById(R.id.entradaNombre);
        return editarTexto.getText().toString();
    }

    public void escribirNombre(String nombre) {
        EditText editarTexto = (EditText) findViewById(R.id.entradaNombre);
        editarTexto.setText(nombre);
    }

    public String crearSaludo(String nombre) {
        String saludo = obtenerSaludoAleatorio();
        return String.format(Locale.getDefault(), saludo, nombre);
    }

    private String obtenerSaludoAleatorio() {
        String saludo;
        Random random = new Random(System.currentTimeMillis());
        int randomNum = random.nextInt(10 + 1);
        switch (randomNum) {
            case 0:
                saludo = getString(R.string.saludo_hola);
                break;
            case 1:
                saludo = getString(R.string.saludo_que_tal);
                break;
            case 2:
                saludo = getString(R.string.saludo_buenos_dias);
                break;
            case 3:
                saludo = getString(R.string.saludo_como_estas);
                break;
            case 4:
                saludo = getString(R.string.saludo_ey);
                break;
            case 5:
                saludo = getString(R.string.saludo_good_morning);
                break;
            case 6:
                saludo = getString(R.string.saludo_conocerte);
                break;
            case 7:
                saludo = getString(R.string.saludo_buenas);
                break;
            case 8:
                saludo = getString(R.string.saludo_hola_soy);
                break;
            case 9:
                saludo = getString(R.string.saludo_dia);
                break;
            default:
                saludo = getString(R.string.saludo_soy_android);
        }
        return saludo;
    }

    public String obtenerSalida(){
        TextView respuesta = (TextView) findViewById(R.id.textoSalida);
        return respuesta.getText().toString();
    }

    public void escribirSalida(String textoSalida) {
        TextView respuesta = (TextView) findViewById(R.id.textoSalida);
        respuesta.setText(textoSalida);
    }

    public void metodoSaludar(View view) {
        escribirSalida(crearSaludo(obtenerNombre()));
    }

    @Override
    public void onSaveInstanceState(Bundle datosGuardados) {
        String salida = obtenerSalida();
        datosGuardados.putString(SALUDO_GUARDADO, salida);
        super.onSaveInstanceState(datosGuardados);
    }

    @Override
    public void onRestoreInstanceState(Bundle datosGuardados) {
        super.onRestoreInstanceState(datosGuardados);
        String salida = datosGuardados.getString(SALUDO_GUARDADO);
        escribirSalida(salida);
    }
}

¿Qué os ha parecido? Mi impresión es que a estas alturas, en un máster en el que uno de los requisitos es tener un cierto nivel de inglés, se debería programar en inglés. Otra de las razones podría ser que los métodos del framework si que están en inglés y aún así los entendemos.

Los nombres de los métodos utilizados serían un inglés básico, como por ejemplo, setName o writeName.

Hasta aquí mi crítica a la práctica que he tenido que hacer hoy  🙂

Pd. ¿Qué os parece?

Backendless BaaS

Buenas a todos,

Después de la retirada de Parse, me decidí por Backendless como mi backend para mis aplicaciones Android. La segunda opción era Firebase, pero finalmente me decidí por Backendless.

En este post quiero explicar todo el procedimiento desde la creación de la cuenta de Backendless, siguiendo por la especificación de los objetos, la descarga de datos siguiendo la API Rest de backendless, hasta la presentación en la aplicación.

El ejemplo que usaremos será sencillo, una simple lista de datos inflada desde nuestro backend. En este caso será una lista con los nombres de nuestros amigos y su fecha de cumpleaños, pero como veremos, cambiar esto por vuestro caso particular es muy sencillo.

1. Preparación de backendless

Lo primero que debemos hacer es crearnos una cuenta en backendless. Una vez logueados en la plataforma, navegaremos hasta la pestaña de ‘Datos’.

El siguiente paso será la creación de la tabla ‘Amigos’ que contendrá tres columnas: nombre, teléfono y fecha de cumpleaños. Para ello, se debe hacer click en el signo ‘+’ situado en el esquina inferior izquierda de la pestaña ‘Datos’.

Una vez creada la tabla ‘Amigos’, crearemos sus columnas. Es conveniente hacer click en el botón rojo ‘Esquema de tablas y permisos’. Añadiremos tres columnas, todas de tipo String. El resultado ha de ser algo parecido a esto:

Captura de pantalla 2016-04-16 a las 19.14.57

Una vez creadas las columnas, se debe rellenar la base de datos. Rellenaremos con datos al azar para este ejemplo. Quedaría algo así:

Captura de pantalla 2016-04-16 a las 19.20.07

2. Android

El proyecto va a constar de una única activity con un recyclerview. Primero vamos a crear el modelo de datos, en nuestro caso Amigo. Es importante que los atributos se llamen iguales que las columnas creadas en backendless. La clase debe tener constructor, además de getters y setters para todos sus atributos.

/**
 * Created by josedelpozo on 16/4/16.
 */
public class Amigo {

    private String nombre;
    private String telefono;
    private String cumple;

    public Amigo(String nombre, String telefono, String cumple) {
        this.nombre = nombre;
        this.telefono = telefono;
        this.cumple = cumple;
    }

    public String getNombre() {
        return nombre;
    }

    public void setNombre(String nombre) {
        this.nombre = nombre;
    }

    public String getTelefono() {
        return telefono;
    }

    public void setTelefono(String telefono) {
        this.telefono = telefono;
    }

    public String getCumple() {
        return cumple;
    }

    public void setCumple(String cumple) {
        this.cumple = cumple;
    }
}

La respuesta de Backendless es un JSON con atributos de offset, un array de Amigos, un link a la siguiente página y el número de total de objectos que aparecen en el array. Es necesario crear una clase como esta:

/**
* Created by josedelpozo on 17/4/16.
*/
public class BackendlessResponse {

private String offset;
private List<Amigo> data;
private String nextPage;
private float totalObjects;

public BackendlessResponse(String offset, List<Amigo> data, String nextPage, float totalObjects) {
this.offset = offset;
this.data = data;
this.nextPage = nextPage;
this.totalObjects = totalObjects;
}

public String getOffset() {
return offset;
}

public void setOffset(String offset) {
this.offset = offset;
}

public List<Amigo> getData() {
return data;
}

public void setData(List<Amigo> data) {
this.data = data;
}

public String getNextPage() {
return nextPage;
}

public void setNextPage(String nextPage) {
this.nextPage = nextPage;
}

public float getTotalObjects() {
return totalObjects;
}

public void setTotalObjects(float totalObjects) {
this.totalObjects = totalObjects;
}
}

Una vez terminado el modelo, comenzamos con la descarga de datos desde backendless. Como comenté, vamos a usar la API Rest. Para facilitarnos la vida, vamos a usar la librería Retrofit 2.0. Así que, se debe añadir a build.gradle las siguiente líneas:

compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'

compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'

El siguiente paso es crear una interfaz, ‘AmigoService’ que define el método a llamar para recuperar nuestros amigos.

/**
 * Created by josedelpozo on 17/4/16.
 */
public interface AmigoService {

    @Headers({"application-id: APP-ID",
            "secret-key: SECRET-KEY",
            "Content-Type: application/json"})
    @GET("/{version}/data/{table-name}")
    Call<BackendlessResponse> getAmigos(@Path("version") String version, @Path("table-name") String table);
}

Vamos a pararnos un momento en este método. Primero se han de definir unas cabeceras que indican una conexión segura hacia nuestra cuenta de backendless. El id de aplicación, y la clave secreta rest se deben copiar y pegar de nuestro dashboard de backendless. Además, definimos que lo que vamos a recibir es un json con los datos de nuestros amigos.

 

Lo siguiente indica que es una petición GET, e indica la dirección a la que haremos dicha petición. En este caso, la versión de nuestra aplicación es v1 y el nombre de la tabla ‘Amigos’.

Este método devuelve un objeto de tipo Call<Object>, siendo object lo que queremos recibir ya parseado. En nuestro caso, BackendlessResponse.

Bien, una vez creado el servicio, hagamos la petición. Como va a ser una aplicación tan sumamente sencilla que solo descargará estos datos, la petición se hará sobre la Activity que mostrará los datos, pero esto no es Clean Code y no se debe hacer así.

Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.backendless.com")
.addConverterFactory(GsonConverterFactory.create())
.build();

final AmigoService service = retrofit.create(AmigoService.class);

Call<BackendlessResponse> call = service.getAmigos("v1", "Amigos");
Callback callbackRetrofit = new Callback<BackendlessResponse>() {
@Override
public void onResponse(Call<BackendlessResponse> call, Response<BackendlessResponse> response) {
adapter.addAll(response.body().getData());
}

@Override
public void onFailure(Call<BackendlessResponse> call, Throwable t) {
Log.d("ERROR","Failure "+t.getMessage());
}
};
call.enqueue(callbackRetrofit);

Este código será el encargado de hacer la petición a la dirección https://api.backendless.com que se especifica en el objecto Retrofit. También se especifica que usaremos Gson para parsear de JSON a nuestro objecto Amigo.

 

Seguidamente creamos el servicio, y llamamos al método definido en la interfaz anterior. Realizaremos la petición de forma asíncrona, eso quiere decir que cuando el servidor nos devuelva la respuesta, nos aparecerá en el callback definido. Si no ha habido fallo, esta aparecerá en onResponse, y en caso contrario en onFailure.

Para mostrar estos datos, es necesario montar un recyclerview sobre nuestro layout, y un adapter que muestre esos datos. Este tema no lo voy a comentar aquí, ya que es muy sencillo y hay miles de tutoriales más detallados. El código si que lo dejaré en Github por si alguien quiere consultarlo.

Captura de pantalla 2016-04-17 a las 10.46.29

¡El resultado es este!

GITHUB

Rating Bar

Hola de nuevo!

Estoy haciendo una app para poder votar a los profesores de la universidad, y así, ser por fin nosotros quienes ponen la nota.

Cuando la aplicación estaba casi terminada, en su primera versión, me di cuenta que la RatingBar de Google no se podía dimensionar en tamaño. O al menos, no lo encontré en su guía, ni me funcionaron ninguno de los consejos de stackoverflow.

Captura de pantalla 2016-03-24 a las 11.21.47

Como veis, el tamaño de las estrellas es demasiado grande. Si intento disminuir su height, para que no sean tan grandes, ya que de ancho si quiero que ocupen todo, ocurre lo siguiente:

Captura de pantalla 2016-03-24 a las 11.24.18

Así que, miré varios ejemplos y me decidí a realizar mi propia RatingBar. Os dejo el link a Github, y si alguien quiere ayudar en el proyecto que no dude!

StarsRatingBar

Saludos!

Aprendiendo Kotlin

Hola a todos!

Hace ya un mes que me cree el blog, y aún no me he atrevido a escribir ningún post.

Llevo ya bastante tiempo siguiendo a @lime_cl, y me interesé por Kotlin, el nuevo lenguaje que Antonio lleva tiempo explicando. Como bien os comenté en el primer post explicatorio sobre lo que iba a ir mi blog, os dije que estaba intentando aprender clean architecture y Dagger 2, así que, se me ocurrió crear un ejemplo con esto desarrollado en Kotlin.

El ejemplo se encuentra en Github, lo podéis obtener aquí. KotlinSeries

Además, tengo pensado desarrollar el mismo código en Java para poder comparar entre ambos lenguajes. Sería interesante observar el número de líneas que te ahorras usando Kotlin.

MVP

La estructura del proyecto está basada en MVP + Repository. El ejemplo muestra una lista de Series que se presentan ordenadas alfabéticamente. Por el momento está lista es rellenada por un fake, es decir, no hay conexión con ningún servidor ni DB, cosa que queda como objetivo para el futuro.

No voy a entrar en detalle de como se estructura el modelo MVP, ya que hay demasiado escrito sobre ello y al final del post os dejaré unas referencias muy interesantes. Pero sí os voy a contar como lo desarrollo yo.

Mis vistas, ya sean activity o fragment, siempre tienen un presenter. Llegado el caso que la vista tenga varias funcionalidades bien distintas, podría tener un presenter por cada funcionalidad. En el proyecto cada Activity tiene un presenter que se encarga de obtener notificaciones de la vista, y devolverle la contestación necesaria.

Cada activity suele tener una funcionalidad, que viene implementada por un caso de uso. La separación de estos casos de uso es estrictamente necesaria para mantener código limpio y fácilmente escalable. El caso de uso se encargará de solicitar al repository los datos que necesita.

En este test, como los datos son fake, no hay servidor, no aparece la función del ‘DataSource’, tema que abordaré cuando le incluya el servidor para descarga de datos.

El esquema sería el siguiente:

Captura de pantalla 2016-03-09 a las 23.10.50

Kotlin vs Java

¿El post no iba de Kotlin?

Sí, en esta parte del post quería enseñaros las principales diferencias que he visto con Java.

Clases

Escribir clases con Java acaba siendo un verdadero coñazo, aunque con el generate… de Android Studio todo se simplifica. Sin embargo, las clases de objeto suelen definir varios setters/getters, además de un método equals o un toString.

En Kotlin vale con una línea para todo lo descrito anteriormente:

data class Serie(val name: String, val description: String, val image: String)

Hebras

En Java la descarga de datos se debe realizar en otra hebra que no sea la Main para no ralentizar la App. Por eso, se debe crear una nueva hebra, hacer la petición de descarga de datos, y una vez que se recibe la respuesta hay que volver a la Main Thread para mostrar los datos. En Kotlin el funcionamiento es el mismo, sólo que con bastante menos líneas de código.

fun getAll(callback : (ArrayList&lt;Serie&gt;?) -&gt; Unit) {
        async() {
            var list = repository.getAll()
            uiThread {
                callback(list)
            }
        }
    }

Esto es posible gracias a la librería Anko.

Callback

Si alguno ha podido ver ya, el método anterior recibe como parámetro un callback. En Java para poder hacer esto era necesario crear una interfaz con un método, y implementarlo donde se llamara a la función. De nuevo, Kotlin elimina código innecesario y la llamada a getAll quedaría de la siguiente forma:

getSeries.getAll(){
    if(it?.size!!.compareTo(0) &amp;gt; 0){
        view?.hideLoading()
        (view as SeriesPresenter.View).showSeries(it)
    }else{
        view?.hideLoading()
        (view as SeriesPresenter.View).showEmptyCase()
    }
}

Cuando aparece la llamada a getAll no se le pasa ningún parámetro, aunque reciba el callback comentado anteriormente. Esto es porque el corchete que se abre seguidamente representa ese callback. La palabra reservada it sirve para recibir el parámetro, en este caso un ArrayList con las Series a mostrar.

Null Safety

Durante el código aparecen ? ó ! ó incluso !!. Gracias al operador ? nos salvamos de la archiconocida NullPointerException que tanta lata da.

Conclusión

Además de estas posibilidades comentadas, Kotlin ofrece otras muchas que aún no he tenido tiempo de investigar y probar.

Me decidí a probarlo por las grandes similitudes que vi con Swift, y he quedado impresionado con las posibilidades y la facilidad en el código. Como todo lo nuevo, supone una curva de aprendizaje al principio lenta, pero es un lenguaje que merece la pena conocer.

Creo que una de sus mayores virtudes podría ser la alternancia en el mismo proyecto de código Java y código Kotlin, ya que cada uno podría ser útil en un aspecto distinto o incluso para el uso de librerías con poca compatibilidad con Kotlin seguir usándolas en Java.

Espero seguir mejorando este ejemplo, y en posteriores post hablar sobre Dagger 2 y funcionalidades nuevas a implementar.

Referencias

Kotlin – Antonio Leiva – http://antonioleiva.com/kotlin/

MVP – Antonio Leiva – http://antonioleiva.com/mvp-android/

My way to clean Android – Christian Panadero – http://panavtec.me/my-way-to-clean-android/