Alexandru V. Simonescu

Software Craftsmanship and Android Development

El lado oscuro de SQLite en Android (Spanish)

Article written in spanish, as exported from my old spanish blog. All new published articles are in english, as is the main language of the blog.

La verdad es que no nos podemos quejar de la documentación del SDK de Android, los chicos de Google se esfuerzan en mantenerla actualizada, publicar entradas en su blog de desarrollo, vídeos en su canal de YouTube y mantener viva a la comunidad. El problema es que a pesar de que tengamos documentación de uso de SQLite en Android de manera oficial, en ningún lado nos dan unas pautas o consejos de uso, las buenas prácticas de toda la vida.

En esta entrada que actualizaré siempre que encuentre algo nuevo, pondré algunas buenas prácticas de uso de SQLite en Android.

Eso quiere decir que lo que ponga aquí va a misa, todo lo contrario, son simplemente consejos que he aprendido y que me quedan por aprender, os animo que comentéis si no estáis de acuerdo con alguno o si tenéis otros nuevos que incluiré en este entrada haciendo mención al autor. Vayamos al grano! ¿Que no nos dicen en la documentación oficial?

  • ¿Donde se debería crear la instancia de SQLiteOpenHelper?
  • ¿Cuantas instancias debería haber?
  • ¿Nos tenemos que preocupar si accedemos a la base de datos desde diferentes hilos?

En teoría SQLite se encarga de bloquear la base de datos a nivel de fichero. Muchos hilos pueden leer pero escribir solo uno, de prevenir eso se encarga el propio bloqueo. También para mantener las cosas en orden, Android implementa bloqueos a nivel de Java en la clase SQLiteOpenHelper.

Si “atacamos” la base de datos desde varios hilos no supondría ningún problema, al menos sobre el papel. ¿Pero que pasaría si intento escribir en la base de datos desde dos conexiones distintas y de forma simultánea? Que solo se haría efectiva una de la escritura, la otra fallará. No va a esperar a que la primera conexión acabe y luego escriba, simplemente no escribirá los cambios. Ni siquiera saltará una excepción, solo un aviso en el Logcat. Otro problema son las distintas conexiones de forma simultánea a la misma base de datos.

SQLiteOpenHelper tiene dos métodos para obtener una conexión a la base de datos de solo lectura o de lectura-escritura. Lo curiosos es que bajo el capó, es la misma conexión. Da igual que solicites un acceso de solo lectura y otro de lectura-escritura, por debajo es la misma conexión para los dos. Entonces.. si usamos el mismo helper desde varios hilos en realidad no estaríamos usando múltiples conexiones. Incluso la clase SQLiteDataBase, de la que cada helper necesite tener una instancia, tiene implementados bloqueos a nivel de código Java.

En ese caso si ejecutamos varias operaciones sobre la base de datos desde diferentes hilos no habrá ninguna mejora. Aquí no estamos para ponerle pegas a las cosas sino para buscar soluciones. Una de las formas de trabajar recomendada en estas situaciones es tener una única conexión a la base de datos y compartirla desde todos los hilos y partes de la aplicación que queramos.

public class DatabaseHelper extends OrmLiteSqliteOpenHelper
{
    private static DatabaseHelper instance;

    public static synchronized DatabaseHelper getHelper(Context context)
    {
        if (instance == null)
            instance = new DatabaseHelper(context);

        return instance;
    }
// Demás métodos...
}

El aparente problema que os puede llamar la atención es que usando esta forma no cerraremos la base de datos, en Android la clase SQLiteOpenHelper es muy estable y en caso de no cerrar la base de datos no pasaría nada, como mucho tendremos un aviso en el Logcat.

Para acceder a la base de datos desde varios hilos, tiene que ser usando la misma conexión.

Usando el ejemplo anterior a la larga en el Logcat puede que nos avisen de un posible “Leak” al no haber cerrado la base de datos. En este enlace hay una posible solución al leak antes mencionado usando un contador de las veces que la base de datos ha sido abierta.

Referencias:

Android SQLite Locking
Single SQLite connection
Concurrent database access
What are the best practices for SQLite for Android
How to make SQLite connections more efficient