viernes, 19 de octubre de 2012

Convertir una cadena que contiene un valor hexa a entero, util para convertir luego a carácter imprimible.

Si alguna vez habéis trabajado directamente con carácteres para hacer conversiones de Unicode y demás ( por ejemplo el típico caso de un parser de emoji ) os habréis dado con la puerta en las narices a la hora de convertir de String a entero y luego poder guardar este formato, tras mucho divagar conseguí la solución y os la expongo aqui.

Integer.decode("0x"+cadena); // Convertir a entero.
String.format("%C",Integer.decode("0x"+cadena))// Convertir al carácter correspondiente.


En cuanto tenga otro rato pondré como pasar una cadena URLEncoded que contiene el valor de un carácter en hexa octal a un valor guardable como cadena para después ser usado con el snippet de arriba.

Happy coding!.

Recorrer drawables obteniendo su id y su nombre, solución a "code too large"

Con el siguiente snippet de código podéis recorrer todos los recursos de la aplicación e ir uno a uno obteniendo su nombre y su identificador de recurso.

Puede parecer una tontería pero es especialmente útil para por ejemplo cargar un array con los recursos que empiezan llamándose de una forma. Por ejemplo es común utilizar esto cuando se necesita un array con imaginad 800 recursos cargados en un cuerpo static.

( de otra forma obtendríais el poco conocido error de compilación "Code too large").


Field[] drawables = R.drawable.class.getFields();
        R.drawable drawableResources = new R.drawable();
        for (Field f : drawables) {
            try {
                if(f.getName().startsWith("ejemplo_"))
                {
                  arrayRecursos.put(f.getName(),f.getInt(drawableResources));   
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }



Happy coding!.

miércoles, 17 de octubre de 2012

Android: Optimización de accesos en SQLite

Os regalo un snippet que os va a volver locos a todos los que desarrolléis una aplicación que tenga un acceso intenso a datos en SQL.

Está sacado de un bugfix incluido en Mozilla Firefox para Android y yo he comprobado en mis carnes el sustancial aumento de rendimiento, en vuestro SQLiteOpenHelper, añadir:



@Override public void onOpen(SQLiteDatabase db) {
// From Honeycomb on, it's possible to run several db
// commands in parallel using multiple connections.
if (Build.VERSION.SDK_INT >= 11) {
db.enableWriteAheadLogging();
}
else
{
// Pre-Honeycomb, we can do some lesser optimizations.
Cursor cursor = null;
try {
cursor = db.rawQuery("PRAGMA synchronous=NORMAL", null);
} finally {
if (cursor != null)
cursor.close();
} cursor = null;
try {
cursor = db.rawQuery("PRAGMA journal_mode=PERSIST", null);
} finally {
if (cursor != null)
cursor.close();
}
}
super.onOpen(db);
}

Según las pruebas de aumento de rendimiento de la gente de Mozilla Firefox, esto por si solo provoca un aumento el 50% de rendimiento y la razón es que por defecto SQLite tiene marcado el pragma synchronous a FULL lo que quiere decir que en cada acceso a la base de datos realiza un bloqueo completo para garantizar que no provoca ningún problema de corrupción.


http://www.sqlite.org/pragma.html
PRAGMA synchronous = NORMAL: "When synchronous is NORMAL (1), the SQLite database engine will still sync at the most critical moments, but less often than in FULL mode. There is a very small (though non-zero) chance that a power failure at just the wrong time could corrupt the database in NORMAL mode. But in practice, you are more likely to suffer a catastrophic disk failure or some other unrecoverable hardware fault."


Para obtener una corrupción en modo NORMAL necesitas una colisión en un checksum de 32-bits por lo que me parece razonablemente descartable la posibilidad xD.

Además se pone el modo del journaling en PERSIST:

PRAGMA journal = PERSIST:
Deleting a file is an expensive operation on many systems. So as an optimization, SQLite can be configured to avoid the delete operation of section 3.11. Instead of deleting the journal file in order to commit a transaction, the file is either truncated to zero bytes in length or its header is overwritten with zeros. Truncating the file to zero length saves having to make modifications to the directory containing the file since the file is not removed from the directory. Overwriting the header has the additional savings of not having to update the length of the file (in the "inode" on many systems) and not having to deal with newly freed disk sectors. Furthermore, at the next transaction the journal will be created by overwriting existing content rather than appending new content onto the end of a file, and overwriting is often much faster than appending.


La gente de Mozilla Firefox ha verificado que en Android es mucho mas lento el comportamiento de TRUNCATE que el de PERSIST; en muchos sistemas embedidos con sistemas de archivos sincronos ocurrirá lo mismo.

Como veis para dispositivos modernos simplemente se activa el Write Ahead Logging, os recomiendo que miréis que es en profundidad pero groso modo podemos decir que se permite leer sin bloqueo y solo se usa el bloqueo para escrituras ( si he dicho una burrada, por favor corregidme ).

Si tenéis algun acceso MUY BRUTO a la base de datos ( imagina insertar 100.000 filas de golpe ); deberíais ( solo para ese acceso ) usar setPerformanceMode que activa el shynchronous a NONE hacer una transacción como dios manda y luego volver a dejarlo como estaba.

Happy coding!

jueves, 11 de octubre de 2012

SQLException: SQL logic error or missing database

Estas usando tu content provider y alegremente recibes esta excepción "SQLException: SQL logic error or missing database" por supuesto no te cuadra de donde puede venir. Pues bien, ojo que los de sqlite han escrito esa excepción con un mensaje algo extraño o quizá es muy genérica. Cuando a mi me ha ocurido es por: - Estas ejecutando una instrucción SQL con un "?" que no se está reemplazando por nada ( ejemplo un WHERE ? que no intercambia el ? por la selección ). - Se está intentando introducir NULL en una columna que es non-NULL Humor: lo cojonudo es que te diga que a lo mejor no existe la base de datos.

lunes, 8 de octubre de 2012

Android Tip: "No me funciona el Log Cat"

Voy a comentar un problema que he tenido hoy con un terminal HUAWEI U8650 el cual me ha chocado bastante, tanto el problema como la solución.

El caso es que estaba yo intentando depurar el teléfono y no me aparecía el logcat en IntelliJ Idea.. y digo.. raro. Como es normal voy a la consola corriendo y pongo adb logcat. A lo que me responde:

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

Entonces ya me interesa el problema, me digo, va a ser de traca; pues tras mucho divagar, ojo a la solución..

1.- Marcas en el teclado de llamada: *#*#2846579#*#*

2.- Esto abrirá un menú oculto de servicio, en el vamos a:
"ProjectMenu" -> "Background Setting" -> "Log Setting"

3.- Configuramos el logcat.
"Log switch" para activarlo o desactivarlo.
"Log level setting" para seleccionar el nivel de log.

4.- Importante, reiniciar el teléfono al terminar.

Nota: Probablemente en este menú se pueda hacer algún destrozo, por lo que no lo trasteéis demasiado si no sabéis que está haciendo.

Fuente: StackOverflow

jueves, 29 de marzo de 2012

Android Tip: .git, doc and META-INF folders

Alguna vez veréis que estas carpetas están dentro de vuestros apk y alguien puede incluso que os diga que sois descuidados y habéis dejado documentación sensible dentro de vuestro apk.

GOLPE DE REMO a toda esa gente.

 Son propios del apk e incluyen control de versiones y documentación necesaria para el propio apk; además ant en su versión 1.8.2 y superiores ya se encarga de excluir .git por ejemplo. Os invito a que lo comprobéis viendo que es lo que hay realmente dentro de esas carpetas ;).

Happy coding.

Android Tips: Unique pending intent based on extras

Seguramente a muchos os ha pasado que al crear varias notificaciones cada una con su pending intent con flag UPDATE CURRENT pues aunque los extras cambian se os reemplazan los pending intent por el último y al darle a la notificación ( da igual cual ) se ejecuta el último pending intent.

Pues bien, hay un truco bien sencillo para que el intent sea único basado en los extras que transporta, al intent que vais a pasar al pending intent le ponéis lo siguiente:


intentDeEjemplo.setData(Uri.parse(intentDeEjemplo.toUri(Intent.URI_INTENT_SCHEME)));


De esta forma el intent será único por los extras que contiene y no se reemplazará a menos que estos sean los mismos.


Happy coding.

miércoles, 28 de marzo de 2012

Android Tips: Inauguramos sección. | Android column '_id' does not exist?

Programando para Android, uno descubre muchos pequeños trucos y soluciones que son dificiles de encontrar y están mal documentadas. Iré añadiendo aquí los tips que vaya viendo para mi propia búsqueda y para a quien le pueda servir.

Tip 1: Android column '_id' does not exist?

En Android para usar cursores como el del SimpleCursorAdapter, este necesita que la tabla tenga una columna "_id"; si no la tiene creadla, si usais otra ponedle un AS ( otra AS _id ) y aquí viene el truco:

Si estais en SDK 7 y usáis una consulta compleja SQL para recuperar los datos y devolvéis las columnas con un identificador de tabla ( C._id, C.nombre por ejemplo ) necesitais obligatoriamente poner el AS en todos los campos ( C._id AS _id, C.nombre AS nombre ) o no encontrará ninguno de ellos.


Tip 2: String.isEmpty();

Si pretendéis que vuestra aplicación funcione en Android 2.1 no hagáis uso de esa función, pues no existe.