ver tutorial studio example español ejemplo datos consultas app java android sqlite android-cursor android-loadermanager

java - tutorial - sqliteopenhelper example



¿Está bien tener una instancia de SQLiteOpenHelper compartida por todas las actividades en una aplicación de Android? (5)

Haga clic aquí para ver la publicación de mi blog sobre este tema.

CommonsWare tiene razón (como de costumbre). Ampliando su publicación, aquí hay un código de muestra que ilustra tres enfoques posibles. Estos permitirán el acceso a la base de datos en toda la aplicación.

Enfoque N. ° 1: subclases `Aplicación`

Si sabe que su aplicación no será muy complicada (es decir, si sabe que solo terminará teniendo una subclase de Application ), puede crear una subclase de Application y hacer que su Actividad principal la amplíe. Esto asegura que una instancia de la base de datos se ejecuta durante todo el ciclo de vida de la aplicación.

public class MainApplication extends Application { /** * see NotePad tutorial for an example implementation of DataDbAdapter */ private static DataDbAdapter mDbHelper; /** * Called when the application is starting, before any other * application objects have been created. Implementations * should be as quick as possible... */ @Override public void onCreate() { super.onCreate(); mDbHelper = new DataDbAdapter(this); mDbHelper.open(); } public static DataDbAdapter getDatabaseHelper() { return mDbHelper; } }

Método n. ° 2: tener `SQLiteOpenHelper` como miembro de datos estáticos

Esta no es la implementación completa, pero debería darle una buena idea sobre cómo diseñar correctamente la clase DatabaseHelper . El método de fábrica estático garantiza que solo exista una instancia de DatabaseHelper en cualquier momento.

/** * create custom DatabaseHelper class that extends SQLiteOpenHelper */ public class DatabaseHelper extends SQLiteOpenHelper { private static DatabaseHelper mInstance = null; private static final String DATABASE_NAME = "databaseName"; private static final String DATABASE_TABLE = "tableName"; private static final int DATABASE_VERSION = 1; private Context mCxt; public static DatabaseHelper getInstance(Context ctx) { /** * use the application context as suggested by CommonsWare. * this will ensure that you dont accidentally leak an Activitys * context (see this article for more information: * http://developer.android.com/resources/articles/avoiding-memory-leaks.html) */ if (mInstance == null) { mInstance = new DatabaseHelper(ctx.getApplicationContext()); } return mInstance; } /** * constructor should be private to prevent direct instantiation. * make call to static factory method "getInstance()" instead. */ private DatabaseHelper(Context ctx) { super(context, DATABASE_NAME, null, DATABASE_VERSION); this.mCtx = ctx; } }

Enfoque n. ° 3: abstrae la base de datos SQLite con un `ContentProvider`

Este es el enfoque que sugeriría. Por un lado, la nueva clase LoaderManager depende en gran medida de ContentProviders, por lo que si desea que una actividad o fragmento implemente LoaderManager.LoaderCallbacks<Cursor> (que le sugiero que aproveche, ¡es mágico!), Tendrá que implementar un ContentProvider para su aplicación. Además, no necesita preocuparse por crear una base de datos de Singleton con ContentProviders. Simplemente llame a getContentResolver() desde la actividad y el sistema se encargará de todo por usted (en otras palabras, no es necesario diseñar un patrón de Singleton para evitar que se creen varias instancias).

¡Espero que ayude!

¿Sería correcto tener una única instancia de SQLiteOpenHelper como miembro de una Aplicación subclase, y tener todas las Actividades que necesitan una instancia de SQLiteDatabase obtener de un único ayudante?


Escribí MultiThreadSQLiteOpenHelper, que es un SQLiteOpenHelper mejorado para aplicaciones de Android donde varios subprocesos pueden abrir y cerrar la misma base de datos sqlite.

En lugar de llamar al método de cierre, los hilos piden el cierre de la base de datos, evitando que un hilo realice una consulta en una base de datos cerrada.

Si cada hilo solicitó el cierre, entonces se realiza realmente un cierre. Cada actividad o hilo (ui-thread y user-threads) realiza una llamada abierta en la base de datos al reanudar, y solicita el cierre de la base de datos al pausar o finalizar.

El código fuente y las muestras están disponibles aquí: https://github.com/d4rxh4wx/MultiThreadSQLiteOpenHelper


Investigué mucho sobre este tema y estoy de acuerdo con todos los puntos mencionados por Commonware. Pero creo que hay un punto importante que todos faltan aquí. La respuesta a esta pregunta depende completamente de su Caso de Uso, así que si su aplicación está leyendo bases de datos a través de múltiples hilos y solo leyendo con Singleton tiene un gran impacto de rendimiento ya que todas las funciones están sincronizadas. y se ejecutan en serie ya que hay una única conexión a la base de datos. El código abierto es genial, por cierto. Puedes profundizar en el código y ver qué está pasando. De eso y algunas pruebas, he aprendido que los siguientes son verdaderos:

Sqlite takes care of the file level locking. Many threads can read, one can write. The locks prevent more than one writing. Android implements some java locking in SQLiteDatabase to help keep things straight. If you go crazy and hammer the database from many threads, your database will (or should) not be corrupted.

Si intenta escribir en la base de datos desde conexiones distintas reales al mismo tiempo, uno fallará. No esperará hasta que el primero esté listo y luego escribirá. Simplemente no escribirá su cambio. Peor aún, si no llama a la versión correcta de inserción / actualización en SQLiteDatabase, no obtendrá una excepción. Recibirá un mensaje en su LogCat, y eso será todo.

El primer problema, conexiones reales, distintas. Lo bueno del código fuente abierto es que puedes profundizar y ver qué está pasando. La clase SQLiteOpenHelper hace algunas cosas divertidas. Aunque hay un método para obtener una conexión de base de datos de solo lectura, así como una conexión de lectura y escritura, bajo el capó, siempre es la misma conexión. Suponiendo que no hay errores de escritura de archivos, incluso la conexión de solo lectura es realmente la única conexión de lectura y escritura. Muy gracioso. Por lo tanto, si usa una instancia de ayuda en su aplicación, incluso desde varios subprocesos, en realidad nunca utiliza múltiples conexiones.

Además, la clase SQLiteDatabase, de la cual cada helper tiene solo una instancia, implementa el bloqueo de nivel java sobre sí mismo. Por lo tanto, cuando esté realmente ejecutando operaciones de base de datos, todas las demás operaciones de base de datos estarán bloqueadas. Entonces, incluso si tienes múltiples hilos haciendo cosas, si lo haces para maximizar el rendimiento de la base de datos, tengo malas noticias para ti. Sin beneficio.

Observaciones interesantes

Si apaga un hilo de escritura, de modo que solo un hilo está escribiendo en el archivo db, pero otra lectura, y ambos tienen sus propias conexiones, el rendimiento de lectura muestra WAY y no veo ningún problema de bloqueo. Eso es algo para perseguir. Todavía no lo he intentado con escribir lotes.

Si va a realizar más de una actualización de cualquier tipo, envuélvala en una transacción. Parece que las 50 actualizaciones que hago en la transacción tardan el mismo tiempo que la actualización 1 fuera de la transacción. Supongo que, fuera de las llamadas de transacción, cada actualización intenta escribir los cambios de db en el disco. Dentro de la transacción, las escrituras se hacen en un bloque, y la sobrecarga de escritura empequeñece la lógica de actualización misma.


Sí, esa es la forma en que debe hacerlo, teniendo una clase de ayuda para las actividades que necesitan una instancia de la Base de datos.


Tener una sola instancia SQLiteOpenHelper puede ayudar a enhebrar casos. Como todos los hilos compartirían la base de datos SQLiteDatabase común, se proporciona sincronización de operaciones.

Sin embargo, no crearía una subclase de Application . Solo tiene un miembro de datos estáticos que sea su SQLiteOpenHelper . Ambos enfoques te brindan algo accesible desde cualquier lugar. Sin embargo, solo hay una subclase de Application , por lo que es más difícil para usted utilizar otras subclases de Application (por ejemplo, GreenDroid requiere un IIRC). Usar un miembro de datos estáticos lo evita. Sin embargo, utilice el Context la Application al crear una instancia de SQLiteOpenHelper estático (parámetro de constructor), para que no se filtre ningún otro Context .

Y, en los casos en que no se trate con múltiples hilos, puede evitar cualquier posible problema de pérdida de memoria con solo usar una instancia de SQLiteOpenHelper por componente. Sin embargo, en la práctica, debe tratar con múltiples hilos (por ejemplo, un Loader ), por lo que esta recomendación solo es relevante para aplicaciones triviales, como las que se encuentran en algunos libros ... :-)