Est-il correct d’avoir une instance de SQLiteOpenHelper partagée par toutes les activités dans une application Android?

Serait-il correct d’avoir une seule instance de SQLiteOpenHelper en tant que membre d’une application sous-classée et de faire en sorte que toutes les activités nécessitant une instance de SQLiteDatabase l’obtiennent de l’un des assistants?

Avoir une seule instance SQLiteOpenHelper peut aider à SQLiteOpenHelper des SQLiteOpenHelper . Comme tous les threads partagent la firebase database SQLiteDatabase commune, la synchronisation des opérations est fournie.

Cependant, je ne ferais pas une sous-classe de l’ Application . Il suffit d’avoir un membre de données statique qui est votre SQLiteOpenHelper . Les deux approches vous offrent quelque chose accessible de n’importe où. Cependant, il existe une seule sous-classe d’ Application , ce qui rend plus difficile l’utilisation d’ autres sous-classes d’ Application (par exemple, GreenDroid nécessite un seul IIRC). L’utilisation d’un membre de données statique évite cela. Toutefois, utilisez le Context Application lors de l’instanciation de ce SQLiteOpenHelper statique (paramètre constructeur), afin de ne pas divulguer un autre Context .

Et, dans les cas où vous ne traitez pas plusieurs threads, vous pouvez éviter tout problème de fuite de mémoire en utilisant une SQLiteOpenHelper instance SQLiteOpenHelper par composant. Cependant, en pratique, vous devriez avoir affaire à plusieurs threads (par exemple, un Loader ), cette recommandation ne concerne donc que les applications sortingviales, telles que celles que l’on trouve dans certains livres … 🙂

Cliquez ici pour voir mon article sur ce sujet.


CommonsWare est sur (comme d’habitude). Dans son article, voici un exemple de code illustrant trois approches possibles. Ceux-ci permettront d’accéder à la firebase database tout au long de l’application.

Approche n ° 1: sous-classement de l’application

Si vous savez que votre application ne sera pas très compliquée (c.-à-d. Si vous savez que vous n’aurez qu’une seule sous-classe d’ Application ), vous pouvez créer une sous-classe d’ Application et étendre votre activité principale. Cela garantit qu’une instance de la firebase database s’exécute tout au long du cycle de vie de l’application.

 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; } } 

Approche n ° 2: avoir SQLiteOpenHelper comme 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 manière de concevoir correctement la classe DatabaseHelper . La méthode de fabrique statique garantit qu’il n’existe qu’une seule instance de DatabaseHelper à tout moment.

 /** * create custom DatabaseHelper class that extends SQLiteOpenHelper */ public class DatabaseHelper extends SQLiteOpenHelper { private static DatabaseHelper mInstance = null; private static final Ssortingng DATABASE_NAME = "databaseName"; private static final Ssortingng 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; } } 

Approche n ° 3: abstraire la firebase database SQLite avec un `ContentProvider`

C’est l’approche que je suggère. D’une part, la nouvelle classe LoaderManager repose fortement sur ContentProviders, donc si vous souhaitez qu’une activité ou un fragment implémente LoaderManager.LoaderCallbacks (dont je vous suggère de tirer parti, 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 firebase database Singleton avec ContentProviders. Appelez simplement getContentResolver() partir de 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 pourra aider!

J’ai écrit MultiThreadSQLiteOpenHelper qui est un SQLiteOpenHelper amélioré pour les applications Android où plusieurs threads peuvent ouvrir et fermer la même firebase database sqlite.

Au lieu d’appeler la méthode close, les threads demandent la fermeture de la firebase database, empêchant un thread d’exécuter une requête sur une firebase database fermée.

Si chaque thread demande la fermeture, une fermeture est effectuée. Chaque activité ou thread (ui-thread et threads utilisateur) effectue un appel ouvert sur la firebase database lors de la reprise et demande la fermeture de la firebase database lors de la pause ou de la fin.

Code source et exemples disponibles ici: https://github.com/d4rxh4wx/MultiThreadSQLiteOpenHelper

J’ai fait beaucoup de recherches sur ce sujet et je suis d’accord avec tous les points mentionnés par les logiciels communs. Mais je pense qu’il y a un point important que tout le monde manque ici, la réponse à cette question dépend entièrement de votre cas d’utilisation, donc si votre application lit des bases de données via plusieurs threads et que seule la lecture de Singleton est énorme. et sont exécutés en série car il existe une seule connexion à la firebase database. En passant, Open source est génial. Vous pouvez creuser directement dans le code et voir ce qui se passe. A partir de cela et de quelques tests, j’ai appris que les choses suivantes sont vraies:

 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 vous essayez d’écrire dans la firebase database à partir de connexions distinctes réelles en même temps, l’une d’entre elles échouera. Il n’attendra pas que le premier soit terminé et écrive ensuite. Il ne sera tout simplement pas écrit votre changement. Pire encore, si vous n’appelez pas la bonne version d’insertion / mise à jour sur la firebase database SQLite, vous ne recevrez pas d’exception. Vous aurez juste un message dans votre LogCat, et ce sera tout.

Le premier problème, réel, des connexions distinctes. La grande chose au sujet du code source ouvert est que vous pouvez creuser directement et voir ce qui se passe. La classe SQLiteOpenHelper fait des choses amusantes. Bien qu’il existe une méthode pour obtenir une connexion à une firebase database en lecture seule, ainsi qu’une connexion en lecture-écriture, sous le capot, c’est toujours la même connexion. En supposant qu’il n’y a pas d’erreurs d’écriture de fichier, même la connexion en lecture seule est vraiment la seule connexion en lecture-écriture. Assez amusant. Ainsi, si vous utilisez une instance d’assistance dans votre application, même à partir de plusieurs threads, vous n’utilisez jamais vraiment plusieurs connexions.

De plus, la classe SQLiteDatabase, dont chaque assistant ne possède qu’une seule instance, implémente le locking de niveau Java sur lui-même. Ainsi, lorsque vous exécutez des opérations de firebase database, toutes les autres opérations de firebase database seront verrouillées. Donc, même si vous avez plusieurs threads à faire des choses, si vous le faites pour optimiser les performances de la firebase database, j’ai de mauvaises nouvelles pour vous. Aucun avantage.

Observations intéressantes

Si vous désactivez un thread d’écriture, un seul thread écrit sur la firebase database, mais une autre lecture, et les deux ont leurs propres connexions, les performances de lecture s’exécutent et je ne vois aucun problème de locking. C’est quelque chose à poursuivre. Je n’ai pas encore essayé avec le batch en écriture.

Si vous souhaitez effectuer plusieurs mises à jour de quelque nature que ce soit, placez-les dans une transaction. Il semble que les 50 mises à jour que je fais dans la transaction prennent le même temps que la mise à jour 1 en dehors de la transaction. Je pense qu’en dehors des appels de transaction, chaque mise à jour tente d’écrire les modifications de la firebase database sur le disque. A l’intérieur de la transaction, les écritures sont effectuées dans un bloc, et la surcharge de l’écriture ne préserve pas la logique de mise à jour elle-même.

Oui, c’est ce que vous devez faire, en ayant une classe d’assistance pour les activités nécessitant une instance de la firebase database.