¿Está bien tener una instancia de sqliteopenhelper compartida por todas las actividades en una aplicación de Android?

StackOverflow https://stackoverflow.com/questions/8888530

Pregunta

¿Estaría bien tener una sola instancia de sqliteopenhelper como miembro de una aplicación subclasiada y tener todas las actividades que necesitan una instancia de sqlitedatabase lo obtengan del único ayudante?

¿Fue útil?

Solución

Tener un solo SQLiteOpenHelper La instancia puede ayudar en los casos de enhebrado. Dado que todos los hilos compartirían lo común SQLiteDatabase, Se proporciona la sincronización de operaciones.

Sin embargo, no haría una subclase de Application. Solo tenga un miembro de datos estáticos que sea su SQLiteOpenHelper. Ambos enfoques le dan algo accesible desde cualquier lugar. Sin embargo, solo hay una subclase de Application, haciendo que sea más difícil para ti usar otro subclases de Application (Por ejemplo, Greendroid requiere un iirc). El uso de un miembro de datos estáticos evita eso. Sin embargo, use el Application Context Al instancias de este estático SQLiteOpenHelper (parámetro de constructor), por lo que no filtra a otros Context.

Y, en los casos en que no se trata con múltiples hilos, puede evitar cualquier posible problema de fuga de memoria simplemente usando uno SQLiteOpenHelper instancia por componente. Sin embargo, en la práctica, tu debería estar tratando 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 ... :-)

Otros consejos

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


Commonsware tiene razón (como siempre). Expandiendo su publicación, aquí hay algún código de muestra que ilustra tres enfoques posibles. Estos permitirán el acceso a la base de datos a lo largo de la aplicación.

Enfoque #1: subclasificación `Aplicación '

Si sabe que su aplicación no será muy complicada (es decir, si sabe que solo terminará teniendo una subclase de Application), entonces puedes crear una subclase de Application Y haga que su actividad principal lo extienda. Esto asegura que una instancia de la base de datos se ejecute 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;
    }
}

Enfoque #2: hacer que `sqliteopenhelper` sea un miembro de datos estáticos

Esta no es la implementación completa, pero debería darle una buena idea sobre cómo diseñar el DatabaseHelper clase correctamente. El método de fábrica estática asegura que exista solo una instancia de base de datos 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 #3: Resumen la base de datos SQLite con un `ContentProvider`

Este es el enfoque que sugeriría. Por un lado, el nuevo LoaderManager La clase se basa en gran medida en ContentProviders, por lo que si desea una actividad o fragmento para implementar LoaderManager.LoaderCallbacks<Cursor> (que le sugiero que aproveche, ¡es mágico!), Deberá implementar un ContentProvider para su aplicación. Además, no necesita preocuparse por hacer un ayudante de base de datos Singleton con ContentProviders. Simplemente llamar getContentResolver() De la actividad y el sistema se encargará de todo por usted (en otras palabras, no hay necesidad de diseñar un patrón singleton para evitar que se creen múltiples instancias).

¡Espero que ayude!

He escrito múltiples múltiples de itinidad, 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 Pide cierre La base de datos, evita que un hilo realice una consulta en una base de datos cerrada.

Si cada hilo solicitó el cierre, entonces realmente se realiza un cierre. Cada actividad o subproceso (subprocesos de UI y subprocesos de usuario) realiza una llamada abierta en la base de datos cuando se reanuda, y solicita cerrar la base de datos al detener o terminar.

Código fuente y muestras 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, por lo que si su aplicación está leyendo bases de datos a través de múltiples hilos Y solo la lectura usando Singleton tiene un gran éxito de rendimiento, ya que todas las funciones se sincronizan y se ejecutan en serie, ya que hay una sola conexión con el código abierto de la base de datos, por cierto. Puede cavar directamente en el código y ver qué está pasando. De eso y algunas pruebas, he aprendido que lo siguiente es cierto:

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 se haga el primero y luego escriba. Simplemente no escribirá su cambio. Peor aún, si no llama a la versión correcta de Insertar/Update en el SQLitedAtabase, no obtendrá una excepción. Recibirá un mensaje en su LogCat, y eso será todo.

El primer problema, conexiones reales y distintas. Lo mejor del código de código 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-escritura, debajo del 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 conexión única de lectura-escritura. Muy gracioso. Entonces, si usa una instancia de ayuda en su aplicación, incluso de varios hilos, nunca De Verdad Usando múltiples conexiones.

Además, la clase SQLitedAtabase, de la cual cada ayudante tiene solo una instancia, implementa el bloqueo del nivel de Java en sí mismo. Entonces, cuando realmente esté ejecutando operaciones de base de datos, todas las demás operaciones de DB se bloquearán. Entonces, incluso si tiene varios hilos haciendo cosas, si lo está haciendo para maximizar el rendimiento de la base de datos, tengo algunas malas noticias para usted. Sin beneficio.

Observaciones interesantes

Si desactiva un hilo de escritura, por lo que solo un hilo está escribiendo en el DB, pero otra lectura, y ambas tienen sus propias conexiones, el rendimiento de lectura dispara en No veo ningún problema de bloqueo. Eso es algo para perseguir. Todavía no lo he intentado con un lote de escritura.

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 toman la misma cantidad de tiempo que la actualización de 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 realizan en un bloque, y la sobrecarga de la escritura eclipsa la lógica de actualización en sí.

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top