Category: Android


Llevo unos días cacharreando para montar una pequeña aplicación en Android. Así que tras un par de pruebas con el emulador, llega el momento de utilizar un dispositivo real, así que he cogido un Samsung Galaxy S (GT-I9000) que liberé hace algún tiempo y que tenía la ROM CreedROM_v11 puesta.

El caso esque al conectar  y lanzar la aplicación, no podía ver nada en el LogCat, sólo un error:

Unable to open log device '/dev/log/main': No such file or directory

Así que tras unas cuantas horas buscando por internet y probando cosas, conseguí dar con la solución. Para variar, es algo bastante simple, resulta que el loggin viene desactivado con esta ROM, así que para activarlo, hay que ir a la aplicación SemaphoreManager y activar el módulo Loggin, luego le damos un reinicio al teléfono y ya podremos ver los logs en LogCat y así depurar nuestra aplicación.  Espero que este pequeño tip sea de ayuda para alguién.

Esta mañana, hablando con un viejo amigo me ha comentado algo sobre App Inventor for Android. Resulta que es una interfaz web para crear pequeñas aplicaciones para Android utilizando Drag&Drop. Cuando me lo ha comentado me he quedado bastante sorprendido, ya que es una gran iniciativa por parte de Google el acercar el acceso de desarrollo simple a más gente. Tras estar un rato toqueteandolo, os puedo decir qué me ha gustado y que no.
Vamos a empezar por lo bueno,

  1. Crear aplicaciones simples es bastante sencillo, como ya he dicho,  Drag & Drop
  2. Viene con soporte para incluir multimedia, acceso a base de datos, lanzar otras activitys, etc.
  3. Simplifca la creación de interfaces gráficas comparado con el desarrollo en eclipse.
  4. No es necesario saber ningún lenguaje de programación para crear la lógica de aplicación. Basta saber un poco de flujo, variables, etc.
  5. Puedes probarlo en tiempo real en un terminal, cada vez que guardas, se actualiza la aplicación en el terminal.

Como se puede ver, son bastantes las ventajas que tiene este sistema, pero le he encontrado las pegas siguientes:

  1. La creación de la interfaz gráfica sigue siendo demasiado simple,(despues de haber probado la de Apple, todas me lo parecen).
  2. De momento no he visto nada para poder recoger datos de un source externo a través de web (un xml, json,etc)
  3. Una vez terminada, solo puedes descargarte un APK
  4. No genera ningun tipo de fuentes con el que puedas añadir más funcionalidades.
  5. Por lo visto, aún hay problemas para subir al market.

Son pegas, que para mi son muy importantes, aunque de momento es solo una Beta, y no se sabe como nos sorprenderan estos chicos de Google de aquí a un año si siguen apostando por este camino para sus terminales.

Preparando una vista de detalles de una aplicación de Android, me he encontradoo con un curioso funcionamiento. La idea de la vista es tener un LinearLayout e ir metiendole en tiempo de ejecución pequeñas vistas que constan de un LinearLayout con un TextView para el titulo y un ListView con unos pasos. Puesto que la vista puede quedar más grande que la pantalla, todo encerrado dentro de un ScrollView. Pero por lo visto hay un pequeño bug, que cuando se encierran un ListView dentro de un ScrollView, en vez de hacer Scroll a toda el conjunto, “obliga” a los ListView a hacer scroll haciendo que su alto sea más pequeño.
Buscando por la red sobre este problema, en todos los foros y listas de desarrollares dicen que es un problema de Google que aún no han solucionado y la mejor “solución” es no poner un ListView dentro de un ScrollView. Aunque por suerte, dí con la siguiente solución , que llamando esta función:

<pre>  public static void setListViewHeightBasedOnChildren(ListView listView) {
        ListAdapter listAdapter = listView.getAdapter();
        if (listAdapter == null) {
            // pre-condition
            return;
        }

        int totalHeight = 0;
        for (int i = 0; i < listAdapter.getCount(); i++) {
            View listItem = listAdapter.getView(i, null, listView);
            listItem.measure(0, 0);
            totalHeight += listItem.getMeasuredHeight();
        }

        ViewGroup.LayoutParams params = listView.getLayoutParams();
        params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
        listView.setLayoutParams(params);
    }
</pre>

Y lo que hace es poner el tamaño del listview justo para que se muestren todos los hijos. Aunque tiene el problema que si tiene muchos hijos con bastante contenido que renderizar, puede tardar bastante.
Aún así, para ir saliendo del paso, va bastante bien.

Si nos vamos a dedicar a hacer aplicaciones para iOs y Android con grandes contenidos locales, posiblemente nos vendría bien una forma de poder pasar rápidamente los datos de un sitio a otro. En iOs los contenidos locales se suelen guardar en un fichero pList, mientras que en Android los xml son la base. O también puede ocurrir que necesitemos consumir desde un servicio web que nos envie los datos en un xml formateado como un plist. En este caso, lo más sencillo es tener un plist (sobre todo si primero hemos desarrollado sobre iOs) y poder leer desde este plist en ambas plataformas. Para ello he encontrado un parser basado en SAX para leer estos plist:


https://github.com/tenaciousRas/android-plist-parser

Aunque es muy comodo, tiene las siguientes desventajas ya que parece que está preparado para leer configuraciones, no utilizar plist como almacen de datos:

  • La raiz ha de ser obligatoriamente un diccionario, no podemos tener un array como raiz.
  • Utiliza tipos propios para representar el plist y los diccionarios
  • El contenido de los array siempre los guarda como Object, con lo cual un casting a String de algo que sabemos que es un String puede causarnos una excepción.
  • Aunque está pensado para Android, no se puede crear con un Resource id de un xml propio

Por el resto, es más o menos bastante cómodo de utilizar. Vamos a poner un ejemplo de como utilizar esta clase:


PListXMLParser parser = new PListXMLParser();
String xml = Utils.getStringFromAsset(ctx,"myData.plist"); //Función que nos devuelve el contenido del xml como un string
PListXMLHandler handler = new PListXMLHandler();

parser.setHandler(handler);
parser.parse(xml);
PList plist =handler.getPlist(); // Objeto Plist que representa al fichero.
Dict d=plist.getRootDictionary();
parseData( d.getConfigurationArray("root"));

Este es un ejemplo de como obtener el contenido, primero obtenemos el contenido del plist en forma de string. Y luego creando un parser y un handler, parseamos el fichero. Ahora solo hay que obtener el objeto del tipo PList y luego el diccionario que es la raiz del arbol de datos. En mi caso, el raiz era un array, pero tuve que envolverlo en un diccionario con un solo elemento llamado root, que es el array. Así que obtengo ese array y se lo paso a un método parseData que se encarga de parsear el contenido del array a mi modelo de datos. A continuación vamos a enseñar el contenido de la función getStringFromAsset que obtiene el contenido de un fichero. Este fichero ha de estar dentro de la carpeta asset de nuestro proyecto.

public static String getStringFromAsset(Context ctx,String filename){
    try{
        AssetManager manager = ctx.getAssets();
        InputStream input= manager.open(filename);
        InputStreamReader in = new InputStreamReader(input, "utf-8");
        StringBuffer xml = new StringBuffer();
        int c =0;
        while( (c = in.read()) != -1){
           xml.append((char)c);
        }
        in.close();
        return xml.toString();
     }catch(Exception e){
         e.printStackTrace();
         return null;
     }
 }

Creo que el código se autoexplica bastante bien. Simplemente obtenemos un AssetManager, que nos permite crear flujos a los ficheros de la carpeta asset y después creamos un InputStreamReader para leer de este fichero. Es importante utilizar un tipo de flujo como este que nos permita establecer la codificación, ya que si no, nos encontraremos con carácteres extraños. Luego solo es ir leyendo caracter por caracter y guardarlos en un stringBuffer. Sinceramente, esta no es la mejor opción, ya que es bastante lenta (en una app mia, cerca de 15 segundos para un plist no muy grande) así que cuando encuentre una forma más eficiente, actualizaré.