27 votes

Android le filetage et le verrouillage de base de données

Nous utilisons AsyncTasks de la base de données access les tables et les curseurs.

Malheureusement, nous voyons parfois des exceptions concernant la base de données verrouillée.

E/SQLiteOpenHelper(15963): Couldn't open iviewnews.db for writing (will try read-only):
E/SQLiteOpenHelper(15963): android.database.sqlite.SQLiteException: database is locked
E/SQLiteOpenHelper(15963):  at     android.database.sqlite.SQLiteDatabase.native_setLocale(Native Method)
E/SQLiteOpenHelper(15963):  at     android.database.sqlite.SQLiteDatabase.setLocale(SQLiteDatabase.java:1637)
E/SQLiteOpenHelper(15963):  at     android.database.sqlite.SQLiteDatabase.<init>(SQLiteDatabase.java:1587)
E/SQLiteOpenHelper(15963):  at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:638)
E/SQLiteOpenHelper(15963):  at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:659)
E/SQLiteOpenHelper(15963):  at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:652)
E/SQLiteOpenHelper(15963):  at android.app.ApplicationContext.openOrCreateDatabase(ApplicationContext.java:482)
E/SQLiteOpenHelper(15963):  at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:193)
E/SQLiteOpenHelper(15963):  at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:98)
E/SQLiteOpenHelper(15963):  at android.database.sqlite.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:158)
E/SQLiteOpenHelper(15963):  at com.iview.android.widget.IViewNewsTopStoryWidget.initData(IViewNewsTopStoryWidget.java:73)
E/SQLiteOpenHelper(15963):  at com.iview.android.widget.IViewNewsTopStoryWidget.updateNewsWidgets(IViewNewsTopStoryWidget.java:121)
E/SQLiteOpenHelper(15963):  at com.iview.android.async.GetNewsTask.doInBackground(GetNewsTask.java:338)
E/SQLiteOpenHelper(15963):  at com.iview.android.async.GetNewsTask.doInBackground(GetNewsTask.java:1)
E/SQLiteOpenHelper(15963):  at android.os.AsyncTask$2.call(AsyncTask.java:185)
E/SQLiteOpenHelper(15963):  at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:256)
E/SQLiteOpenHelper(15963):  at java.util.concurrent.FutureTask.run(FutureTask.java:122)
E/SQLiteOpenHelper(15963):  at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:648)
E/SQLiteOpenHelper(15963):  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:673)
E/SQLiteOpenHelper(15963):  at java.lang.Thread.run(Thread.java:1060)

Parviendrez-vous à en avoir un exemple de code qui écrit à une base de données à partir d'un autre thread que celui de la lecture et comment pouvons-nous assurer la sécurité des threads.

Une suggestion que j'ai eu est d'utiliser un ContentProvider, ce qui permettrait de gérer l'accès de la base de données à partir de plusieurs threads. Je vais regarder cela, mais est-ce la méthode recommandée de la manipulation d'un tel problème? Il semble plutôt lourd étant donné que nous parlons de devant ou de derrière.

28voto

Pandalover Points 1420

Nous avons utilisé un ContentProvider à la fin. Cela est apparu clairement les problèmes.

20voto

CommonsWare Points 402670

Vous pouvez utiliser Java standard de synchronisation (par exemple, synchronized(this) {} ) autour de votre base de données d'accès.

14voto

ubzack Points 1214

J'ai résolu cette même exception en s'assurant simplement que tous mes base de données s'ouvre avons ferme, et (plus important) pour assurer cette, rendant la portée de chaque instance de base de données en local UNIQUEMENT à la méthode qui en a besoin. ContentProvider est un bon, coffre-fort classe à utiliser lors de l'accès à une base de données à partir de plusieurs threads, mais aussi assurez-vous que vous êtes en utilisant de bonnes db pratiques:

  • Garder les instances db local (pas de SQLiteDatabase les membres de la classe!)
  • appelez close() sur le db dans la même méthode dans laquelle il est ouvert
  • appelez close() sur les curseurs que vous obtenez à partir de la db
  • écouter LogCat pour toutes les plaintes que SQLiteDatabse pourrait avoir

8voto

Moss Points 3813

Prendre en compte le fait que les bases de données SQLite sont à base de fichier et ne sont pas destinées à être en mesure d'être accessible dans un multi-termes de processus. Le meilleur de la procédure sur le mélange SQLite avec le multi-traitement à l'aide de sémaphores (acquérir(), release()) dans chaque base de données liées à l'accès.

Si vous créez un Db wrapper qui acquiert/communiqués d'un sémaphore global de votre DB accès thread-safe. En effet, cela signifie que vous pourriez obtenir une bootleneck parce que vous êtes des files d'attente de l'accès à la DB. Si en plus vous ne pouvez les envelopper l'accès à l'aide de sémaphores si c'est une opération qui modifie la base de données, si bien que vous êtes alterin la db, personne ne sera en mesure d'y accéder et d'attendre jusqu'à ce que le processus d'écriture a été accomplie.

8voto

italo Points 3353

Avant de peu de code, nous allons reprendre certaines des approches:

  • Les sémaphores: de loin la meilleure solution présentée. Il va au cœur du problème: le partage de la ressource! Il permettra de traiter le verrouillage de l'accès à la base, éviter les conflits d'intérêts (database is locked).

  • Java synchronisation: Une sorte de sémaphore de la mise en œuvre, mais moins sofisticated. À l'aide de synchronized vous ne sera pas facile à résoudre certains cas liés à des transactions.

  • ContentProvider: mettre en oeuvre ContentProvider résoudre le problème seulement pour certains cas (ou balayer le problème sous le tapis). Vous aurez encore confrontés aux mêmes problèmes. La différence est que, ContentProvider modèle va vous guider pour ne pas faire certains commum des erreurs lors de l'accès à la base de données Sqlite. Le ContentProvider docs dit: "Vous n'avez pas besoin d'un prestataire pour l'utilisation d'une base de données SQLite si l'utilisation est entièrement à l'intérieur de votre propre application."

  • Presque obligatoire: garder les instances db local, appel close() sur le db dans la même méthode dans laquelle il est ouvert à l'aide d' finally des déclarations, close() sur les curseurs à l'aide finally des déclarations, etc sont presque obligatoire pour éviter les problèmes à l'aide de Sqlite.

Nous allons montrer un exemple de l'sémaphore solution présentée par la Mousse, que j'ai pris de CL. et improoved pour couvrir les transactions.

class DataAccess {
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private final Lock r = rwl.readLock();
    private final Lock w = rwl.writeLock();

    public Data readSomething(int id) {
        Cursor c = null;
        r.lock();
        try {
            c = getReadableDatabase().query(...);
            return c.getString(0);
        } finally {
            if (c != null) c.close();
            r.unlock();
        }
    }

    public void changeSomething(int id, int value) {
        w.lock();
        try {
            getWritableDatabase().update(...);
        } finally {
            w.unlock();
        }
    }

    private void beginTransactionWithSemaphores() {
        getWritableDatabase().beginTransactionWithListener(new SQLiteTransactionListener() {
            @Override
            public void onBegin() {
                w.lock();
            }

            @Override
            public void onRollback() {
                w.unlock();
            }

            @Override
            public void onCommit() {
                w.unlock();
            }
        });
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X