web-dev-qa-db-fra.com

Android Base de données SQLite Quand fermer)

Je travaille avec une base de données SQLite sur Android. Mon gestionnaire de base de données est un singleton et ouvre maintenant une connexion à la base de données lorsqu'elle est initialisée. Il est prudent de laisser la base de données ouverte tout le temps afin que, lorsque quelqu'un appelle ma classe pour travailler avec la base de données, celle-ci soit déjà ouverte? Ou devrais-je ouvrir et fermer la base de données avant et après chaque accès est nécessaire. Y at-il un mal à le laisser ouvert tout le temps?

Merci!

93
w.donahue

je le garderais ouvert tout le temps et le fermerais dans une méthode de cycle de vie telle que onStop ou onDestroy. De cette façon, vous pouvez facilement vérifier si la base de données est déjà utilisée en appelant isDbLockedByCurrentThread ou isDbLockedByOtherThreads sur l'objet unique SQLiteDatabase à chaque fois avant de l'utiliser. cela empêchera de multiples manipulations dans la base de données et sauvera votre application d'un crash potentiel

ainsi, dans votre singleton, vous pourriez avoir une méthode comme celle-ci pour obtenir votre unique objet SQLiteOpenHelper:

private SQLiteDatabase db;
private MyDBOpenHelper mySingletonHelperField;
public MyDBOpenHelper getDbHelper() {
    db = mySingletonHelperField.getDatabase();//returns the already created database object in my MyDBOpenHelper class(which extends `SQLiteOpenHelper`)
    while(db.isDbLockedByCurrentThread() || db.isDbLockedByOtherThreads()) {
        //db is locked, keep looping
    }
    return mySingletonHelperField;
}

aussi, chaque fois que vous voulez utiliser votre objet open helper, appelez cette méthode getter (assurez-vous qu'il est threadé)

une autre méthode de votre singleton peut être (appelée CHAQUE FOIS avant d'essayer d'appeler le getter ci-dessus):

public void setDbHelper(MyDBOpenHelper mySingletonHelperField) {
    if(null == this.mySingletonHelperField) {
        this.mySingletonHelperField = mySingletonHelperField;
        this.mySingletonHelperField.setDb(this.mySingletonHelperField.getWritableDatabase());//creates and sets the database object in the MyDBOpenHelper class
    }
}

vous souhaiterez peut-être également fermer la base de données dans le singleton:

public void finalize() throws Throwable {
    if(null != mySingletonHelperField)
        mySingletonHelperField.close();
    if(null != db)
        db.close();
    super.finalize();
}

si les utilisateurs de votre application ont la possibilité de créer de nombreuses interactions de base de données très rapidement, vous devez utiliser quelque chose comme ce que j'ai démontré ci-dessus. mais s'il y a peu d'interactions avec la base de données, je ne m'en soucierais pas, je créerais et fermerais la base de données à chaque fois.

60
binnyb

Pour l'instant, il n'est pas nécessaire de vérifier si la base de données est verrouillée par un autre thread. Lorsque vous utilisez singleton SQLiteOpenHelper dans chaque thread, vous êtes en sécurité. De la documentation isDbLockedByCurrentThread:

Le nom de cette méthode vient du moment où une connexion active à la base de données signifiait que le thread maintenait un verrou réel sur la base de données. De nos jours, il n'y a plus de véritable "verrou de base de données", bien que les threads puissent se bloquer s'ils ne peuvent pas acquérir une connexion de base de données pour effectuer une opération particulière.

isDbLockedByOtherThreads est obsolète depuis le niveau 16 de l'API.

20
mixel

En ce qui concerne les questions:

Mon gestionnaire de base de données est un singleton et ouvre maintenant une connexion à la base de données lorsqu'elle est initialisée.

Nous devrions diviser 'DB d'ouverture', 'ouvrir une connexion'. SQLiteOpenHelper.getWritableDatabase () donne une base de données ouverte. Mais nous n’avons pas à contrôler les connexions comme cela se fait en interne.

Il est prudent de laisser la base de données ouverte tout le temps afin que, lorsque quelqu'un appelle ma classe pour travailler avec la base de données, celle-ci soit déjà ouverte?

Oui, ça l'est. Les connexions ne sont pas bloquées si les transactions sont correctement fermées. Notez que votre base de données sera également fermée automatiquement si GC la finalise.

Ou devrais-je ouvrir et fermer la base de données avant et après chaque accès est nécessaire.

La fermeture de l'instance SQLiteDatabase n'a rien d'extraordinaire à l'exception de la fermeture de connexions, mais c'est un inconvénient pour le développeur s'il existe des connexions à ce moment. De même, après SQLiteDatabase.close (), SQLiteOpenHelper.getWritableDatabase () renverra une nouvelle instance.

Y at-il un mal à le laisser ouvert tout le temps?

Non, il n'y en a pas. Notez également que la fermeture de la base de données à un moment sans lien et le filetage, par ex. dans Activity.onStop () peut fermer des connexions actives et laisser les données dans un état incohérent.

12
matreshkin

Android 8.1 a une méthode SQLiteOpenHelper.setIdleConnectionTimeout(long) qui:

Définit le nombre maximal de millisecondes pendant lequel une connexion SQLite est autorisée à être inactive avant d'être fermée et supprimée du pool.

https://developer.Android.com/reference/Android/database/sqlite/SQLiteOpenHelper.html#setIdleConnectionTimeout (long)

1
Mark

Du point de vue des performances, le moyen optimal consiste à conserver une instance unique de SQLiteOpenHelper au niveau de l'application. L'ouverture de la base de données peut être coûteuse et constitue une opération de blocage. Par conséquent, cela ne devrait pas être fait sur le thread principal et/ou dans les méthodes de cycle de vie d'activité.

méthode setIdleConnectionTimeout () (introduite dans Android 8.1) peut être utilisée pour libérer RAM lorsque la base de données n'est pas utilisée. Si inactif timeout est défini, les connexions à la base de données seront fermées après une période d'inactivité, c'est-à-dire lorsque la base de données n'a pas été utilisée. Les connexions sont rouvertes de manière transparente dans l'application, lorsqu'une nouvelle requête est exécutée.

En plus de cela, une application peut appeler releaseMemory () lorsqu'elle passe en arrière-plan ou détecte une pression de mémoire, par ex. dans onTrimMemory ()

1
ejboy

Vous pouvez également utiliser ContentProvider. Il fera ce genre de choses pour vous.

0
Gregory Buiko