lunes, 16 de julio de 2012

Extender PhoneGap/Cordova creando un plugin

Después de jugar un rato con Cordova (antiguo PhoneGap), una de las cosas que más me ha gustado es lo fácil que es saltar del mundo HTML al mundo nativo utilizando los plugins que incluye Cordova o incluso creando tus propios plugins.

Arquitectura de PhoneGap/Cordova

OJO: Todo lo que voy a contar está basado en la versión para Android de Cordova. Supongo que, en líneas generales, será similar en el resto de versiones (iOS, BlackBerry, etc.).
La arquitectura global de Cordova es algo así:
Arquitectura de PhoneGap/Cordova
Arquitectura de PhoneGap/Cordova
Tenemos por una parte el mundo HTML y por otra el mundo nativo, cada uno con sus componentes. La única vía de comunicación entre ambos mundos es a través de Cordova. Cordova tiene 2 partes, una Javascript que reside en el mundo HTML y otra en Java (en el caso de Android) que reside en el mundo nativo.
Toda la funcionalidad existente en Cordova (GPS, acelerómetro, lista de contactos, …) está implementada como un plugin, con su parte Javascript y su parte nativa.
Podemos usar el mismo sistema que los plugins de Cordova para añadir nuevas funcionalidades. Para eso necesitaremos:
  • Crear la parte Javascript del plugin, que será el interface disponible para nuestra aplicación web.
  • Crear la parte nativa del plugin, que será invocada desde el código Javascript y contendrá la implementación de la funcionalidad.
Vamos a ver un ejemplo sencillo, creando un plugin que nos permita iniciar desde la página web la navegación a un destino usando Google Navigation o la aplicación que tenga el usuario registrado para ello.

Creando el interface en Javascript

El código que se necesita para crear el plugin en Javascript es el siguiente:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var GoTo = function() {
};
 
GoTo.prototype.location = function(address, successCallback, failureCallback) {
    return PhoneGap.exec(
            successCallback,
            failureCallback,
            'GoTo',      // Nombre con el que está registrado el plugin Java
            'location'// Acción invocada
            [address]);
};
 
cordova.addConstructor(function() {
    cordova.addPlugin("goTo", new GoTo());
});
Sólo hay que crear un objeto con los métodos que queremos exponer, en este caso tenemos un objeto GoTo que expone un único método location que podremos invocar indicando la dirección a la queremos navegar y dos callbacks que serán invocadas de forma asíncrona cuando se complete la invocación, una para el caso de que todo funcione, successCallback, y otra por si hay algún problema, failureCallback.
La implementación del método usa PhoneGap para lanzar, a través de Cordova, la invocación de la parte nativa del plugin. Para pasar parámetros hacia la parte nativa usaremos un array que será serializado como JSON.
Por último, debemos añadir a cordova el plugin, lo que hacemos con el último fragmento de código:
1
2
3
cordova.addConstructor(function() {
    cordova.addPlugin("goTo", new GoTo());
});
Con esto se añade una propiedad goTo al objeto plugins y podremos usarlo fácilmente desde Javascript:
1
plugins.goTo.location("Plaza Mayor, Madrid, España");

Implementando la parte nativa

Para implementar la parte nativa habrá que usar el lenguaje nativo del dispositivo. En el caso de Android, esto implica escribir una clase Java, que en nuestro ejemplo tendrá el siguiente aspecto:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class GoToPlugin extends Plugin {
    @Override
    public PluginResult execute(String action, JSONArray data, String callbackId) {
 
        try {
            Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("google.navigation:q=" + data.getString(0)));
            this.ctx.startActivity(i);
            return new PluginResult(Status.OK);
        }
        catch (Exception e) {
            System.err.println(e.getStackTrace());
            return new PluginResult(Status.ERROR);
        }
    }
}
Olvidemos por un momento la pésima gestión de errores del ejemplo y centrémonos en el plugin. La parte nativa de un plugin es una clase que extiende Plugin en cuyo método execute debemos implementar la funcionalidad del plugin.
Los parámetros de execute incluyen:
  • La acción a realizar. En este caso sólo tenemos una, location, pero podríamos tener varias y en el execute realizar distintas acciones en función del valor del parámetro.
  • Los parámetros de la acción, que recibimos encapsulados en un array JSON. En nuestro caso el array tiene un único elemento que es un String con la dirección a la que queremos dirigirnos.
  • El id de las callback asociadas a la invocación. En este ejemplo no lo utilizamos.
La implementación de la funcionalidad no tiene mucha historia si has programado algo para Android. Únicamente estamos definiendo un Intent para indicar que queremos navegar a una ubicación y dejamos al sistema que inicie ese Intent, con lo que buscará la aplicación asociada y la activará.
Para registrar el plugin en Cordova es necesario modificar el fichero /res/xml/plugins.xml del proyecto de Eclipse y añadir nuestro plugin:
1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<plugins>
    ...
    <plugin name="GoTo" value="koalite.cordova.GoToPlugin"/>
</plugins>
Es importante recordar que el nombre asignado al plugin en el fichero plugins.xml es el nombre que debemos usar luego desde Javascript al usar el método PhoneGap.exec(...).

Conclusiones

Cuando se desarrollan aplicaciones sobre plataformas no nativas es fundamental tener algún mecanismo para poder ejecutar código nativo y acceder a funcionalidades que no estén cubiertas por el framework que estás utilizando. Creedme, he padecido mucho con .NETCF y sé de lo que hablo. Llega un momento en que Platform Invoke se convierte en uno de tus mejores amigos.
En Cordova la creación de plugins para salir del mundo HTML+JS+CSS y llegar hasta el dispositivo físico es francamente sencillo y está muy bien resuelto, lo que da mucha tranquilidad. No hay nada peor que saber que la plataforma para la que estás desarrollando soporta algo pero tu lenguaje no, y con el sistema de plugins de Cordova es sencillo remediar esa situación cuando se presenta

No hay comentarios:

Publicar un comentario