web-dev-qa-db-fra.com

Utilisation du modèle de conception Singleton pour SQLiteDatabase

Je suis plutôt novice sur Android et je travaille sur une application simple pour obtenir une expérience de base. Mon application est assez simple et se compose entre autres d'un récepteur de diffusion et de certaines activités. Les deux composants utilisent une seule base de données, donc en théorie, il pourrait arriver que les deux essaient d'accéder simultanément à la base de données.

Actuellement, j'instancie simplement l'objet db (qui est une classe d'assistance SQLite db) chaque fois que j'en ai besoin et j'exécute les opérations nécessaires: requête, insertion, etc.

D'après ce que j'ai lu ici et dans certains autres documents, cela a le problème d'obtenir une exception "db verrouillée" au cas où la base de données est accédée simultanément, donc une meilleure approche serait d'avoir une seule instance de cet objet db donc tout les composants utilisent à tout moment la même connexion db.

Le raisonnement ci-dessus est-il correct? Un singleton serait-il alors une solution suffisante pour cela? Je sais que certains puristes peuvent s'y opposer, mais veuillez noter qu'il s'agit d'une application assez simple, donc je peux me permettre de faire des choses que je ne ferais pas dans d'autres cas.

Sinon, quelle serait une meilleure option? J'ai lu sur l'utilisation du fournisseur de contenu, mais ce serait trop pour cela, en plus du fait que je ne suis pas intéressé à partager les données avec d'autres activités. J'ai en effet lu ce post et l'ai trouvé plutôt utile.

70
Dan

Cliquez ici pour voir mon article de blog sur ce sujet.


Voici un exemple de code qui illustre trois approches possibles. Ceux-ci permettront d'accéder à la base de données tout au long de l'application.

Approche n ° 1: avoir `SQLiteOpenHelper` être un membre de données statique

Ce n'est pas l'implémentation complète, mais cela devrait vous donner une bonne idée sur la façon de concevoir correctement la classe DatabaseHelper. La méthode de fabrique statique garantit qu'il n'existe qu'une seule instance DatabaseHelper à la fois.

/**
 * 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://Android-developers.blogspot.nl/2009/01/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;
    }
}

Approche n ° 2: abstraire la base de données SQLite avec un "ContentProvider"

C'est l'approche que je suggérerais. D'une part, la nouvelle classe CursorLoader nécessite ContentProviders, donc si vous voulez qu'une activité ou un fragment implémente LoaderManager.LoaderCallbacks<Cursor> Avec un CursorLoader (que je vous suggère de prendre avantage de, c'est magique!), vous devrez implémenter un ContentProvider pour votre application. De plus, vous n'avez pas à vous soucier de créer un assistant de base de données Singleton avec ContentProviders. Appelez simplement getContentResolver() depuis l'Activité et le système s'occupera de tout pour vous (en d'autres termes, il n'est pas nécessaire de concevoir un modèle Singleton pour empêcher la création de plusieurs instances).

J'espère que cela t'aides!

96
Alex Lockwood

Je n'ai jamais lu sur l'utilisation d'un singleton pour accéder à une base de données sur Android. Pourriez-vous fournir un lien à ce sujet?.

Dans mes applications, j'utilise des objets dbhelper simples, pas des singletons, je pensais que c'est plus le travail du moteur sql pour s'assurer que db n'est pas verrouillé, pas le travail de vos classes Android, et cela fonctionne assez bien pour ma plus grande application de taille moyenne.

Mise à jour # 1: en regardant la référence que vous avez donnée, il semble que le problème ne concerne pas du tout l'utilisation de différentes instances d'un dbhelper. Même une seule instance peut rencontrer des problèmes d'accès aux bases de données: le problème provient d'un accès simultané. Ainsi, la seule façon d'assurer un accès correct à la base de données par différents threads est d'utiliser des mécanismes de synchronisation de threads simples (méthodes ou blocs synchronized), et cela n'a presque rien à voir avec l'utilisation d'un singleton.

Mise à jour # 2: le deuxième lien que vous fournissez montre clairement qu'il est nécessaire d'avoir des objets dbhelper singleton dans le cas où plusieurs threads écrivent simultanément dans une base de données. Cela peut se produire si vous effectuez des opérations SQL (insertions/mises à jour/suppressions) à partir d'AsyncTasks par exemple. Dans ce cas, un objet singleton dbhelper mettrait simplement toutes les opérations sql dans une sorte de pipeline et les exécuterait dans l'ordre.

Cette solution pourrait être plus facile à implémenter que l'utilisation d'une synchronisation de thread appropriée à l'aide de méthodes synchronisées en Java. En fait, je pense qu'il devrait y avoir plus d'accentuation quelque part dans Android docs sur ce problème et l'utilisation d'un assistant db singleton pourrait être encouragée.

Merci pour cette belle question et les suivis.

20
Snicolas