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 comme membre d'une Application subclassée, et que toutes les activités qui ont besoin d'une instance de SQLiteDatabase l'obtiennent de l'un des assistants?
5 réponses
ayant un simple SQLiteOpenHelper
l'instance peut aider dans les cas de threading. Puisque tous les fils partageraient le commun SQLiteDatabase
, la synchronisation des opérations est fournie.
cependant, je ne ferais pas une sous-classe de Application
. Juste une donnée membre statique qui est votre SQLiteOpenHelper
. Les deux approches vous donnent quelque chose d'accessible de n'importe où. Cependant, il n'existe qu' sous-classe de Application
, rendant plus difficile pour vous d'utiliser autres sous-classes de Application
(par exemple, GreenDroid nécessite un IIRC). L'utilisation d'un élément de données statique permet d'éviter cela. Cependant, ne pas utiliser le Application
Context
lors de l'instanciation de cette statique SQLiteOpenHelper
(paramètre constructeur), de sorte que vous ne fuyez pas un autre Context
.
et, dans les cas où vous n'avez pas affaire à plusieurs threads, vous pouvez éviter tout problème de fuite de mémoire en utilisant un seul SQLiteOpenHelper
exemple par composant. Cependant, dans la pratique, vous devrait traiter avec plusieurs threads (par ex., Loader
), de sorte que cette recommandation n'est pertinente que pour des applications triviales, comme celles que l'on trouve dans certains livres... : -)
Cliquez ici pour voir mon billet de blog sur ce sujet.
CommonsWare, c'est sur (comme d'habitude). En développant sur son poste, voici quelques exemples de code qui illustre trois approches possibles. Cela permettra d'accéder à la base de données tout au long de l'application.
approche #1: subclassing ' Application`
si vous savez que votre application ne sera pas très compliquée (i.e. si vous savez que vous n'aurez qu'une seule sous-classe) Application
), vous pouvez créer une sous-classe de Application
et faites étendre votre activité principale. Cela garantit qu'une instance de la base de données est en cours d'exécution 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 #2: `SQLiteOpenHelper` être une donnée membre 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 le DatabaseHelper
classe correctement. La méthode d'usine statique assure qu'il existe une seule base de données par instance à tout moment.
/**
* 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;
}
}
Approche n ° 3: résumé de la base de données SQLite avec un " ContentProvider`
C'est l'approche que je suggérerais. Pour l'un, la nouvelle LoaderManager
class s'appuie fortement sur ContentProvider, donc si vous voulez qu'une activité ou un Fragment implémente LoaderManager.LoaderCallbacks<Cursor>
(dont je vous conseille de profiter, c'est magique!), vous aurez besoin de mettre en œuvre un ContentProvider
pour votre application. De plus, vous n'avez pas à vous soucier de faire un Assistant de base de données Singleton avec des fournisseurs de contenu. Appelez simplement getContentResolver()
de L'activité et le système prendra soin de tout pour vous (en d'autres termes, il n'y a pas besoin de concevoir un modèle Singleton pour empêcher la création de plusieurs instances).
j'Espère que ça aide!
J'ai écrit MultiThreadSQLiteOpenHelper qui est un SQLiteOpenHelper amélioré pour les applications Android où plusieurs threads pourraient ouvrir et fermer la même base de données sqlite.
au lieu d'appeler la méthode close, threads demander de fermeture la base de données, empêchant un thread d'effectuer une requête sur une base de données fermée.
si chaque thread demande la fermeture, alors une fermeture est en fait effectuée. Chaque activité ou fil (ui-thread et user-threads)) effectue un appel ouvert sur la base de données lors de la reprise, et demande de fermer la base de données lors de l'arrêt ou de la fin.
code Source et échantillons disponibles ici: https://github.com/d4rxh4wx/MultiThreadSQLiteOpenHelper
j'ai fait beaucoup de recherche sur ce sujet et je suis d'accord avec tous les points mentionnés par commonware . 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 seule la lecture utilisant Singleton a un énorme succès de performance que toutes les fonctions sont synchronisées et sont exécutées en série comme il ya une seule connexion à la base de données Open source est grande, par la manière. Vous pouvez creuser dans le code et voir ce qui se passe. A partir de cela et de quelques tests, j'ai appris que ce qui suit est vrai:
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 base de données à partir de connexions distinctes réelles en même temps, l'une d'elles échouera. Il n'attendra pas que le premier soit fait et ensuite écrire. Il sera tout simplement pas écrire votre changement. Pire, si vous n'appelez pas la bonne version d'insert/update sur la base de données Sqlited, vous n'obtiendrez pas d'exception. Vous aurez juste obtenez un message dans votre LogCat, et ce sera tout.
le premier problème, des connexions réelles et distinctes. La grande chose à propos du code source ouvert est que vous pouvez creuser et voir ce qui se passe. La classe SQLiteOpenHelper fait de drôles de choses. Bien qu'il existe une méthode pour obtenir une connexion de base de données 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 ait pas d'erreurs d'écriture de fichier, même la connexion en lecture seule est seule, en lecture-écriture de connexion. Assez drôle. Ainsi, si vous utilisez une instance helper dans votre application, même à partir de plusieurs threads, vous ne vraiment en utilisant plusieurs connexions.
de plus, la classe SQLiteDatabase, dont chaque helper n'a qu'une seule instance, implémente java level locking sur elle-même. Donc, quand vous exécutez réellement des opérations de base de données, toutes les autres opérations de base de données seront verrouillées. Donc, même si vous avez plusieurs fils qui font des choses, si vous le faites pour maximiser les performances de base de données, j'ai de mauvaises nouvelles pour vous. Aucun avantage.
Observations Intéressantes
si vous éteignez un fil d'écriture, donc un seul fil est en train d'écrire à la db, mais une autre lecture, et les deux ont leurs propres connexions, la performance de lecture monte en flèche et Je ne vois aucun problème de serrure. C'est quelque chose de poursuivre. Je n'ai pas encore essayé avec Écrire batching.
Si vous envisagez d'effectuer des plus qu'une mise à jour de toute nature, de l'envelopper 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 à l'extérieur de la transaction. À mon avis, en dehors des appels de transaction, chaque mise à jour tente d'écrire les modifications de la base de données sur le disque. À l'intérieur de la transaction, les Écritures se font en un seul bloc, et les frais généraux d'écriture éclipsent la logique de mise à jour elle-même.
Oui, c'est la façon dont vous devriez aller à ce sujet, ayant une classe d'assistance pour les activités qui ont besoin d'une instance de la Base de données.