Question

Nous utilisons AsyncTasks aux tables et des curseurs de base de données d'accès.

Malheureusement, nous voyons parfois des exceptions en ce qui concerne la base de données étant 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)

Quelqu'un at-il un exemple général pour le code qui écrit à une base de données à partir d'un fil différent de celui de lecture et comment pouvons-nous assurer la sécurité du fil.

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

Était-ce utile?

La solution

Je résolu cette même exception que en vous assurant que toute ma base de données ouvre avoir ferme, et (surtout) pour assurer ce, ce qui rend la portée locale de chaque instance de base de données uniquement à la méthode qui en a besoin. ContentProvider est une bonne classe en toute sécurité à utiliser lors de l'accès à un db à partir de plusieurs threads, mais aussi vous faire vous utilisez les bonnes pratiques db:

  • Gardez locales db cas (pas de membres de la classe SQLiteDatabase!)
  • appel close() sur la db dans la même méthode dans laquelle il est ouvert
  • appel close() sur les curseurs que vous obtenez de la db
  • écouter LogCat pour toutes les plaintes SQLiteDatabse pourrait avoir

Autres conseils

Nous avons utilisé un ContentProvider à la fin. Cela semble éclaircir les problèmes.

Avant un code, nous allons reprendre quelques-uns des Approches:

  • sémaphores : de loin la meilleure solution présentée. Il va au cœur du problème: le partage des ressources! Elle traitera le verrouillage de l'accès base de données, éviter les conflits (database is locked).

  • synchronisation Java : Une sorte de mise en œuvre sémaphores, mais moins Sophistiqué. L'utilisation synchronized vous ne résoudra pas facilement certains cas impliquant des transactions.

  • ContentProvider : mettre en œuvre ContentProvider résoudre le problème que pour certains cas (ou balayer le problème sous le tapis). Vous encore faire face aux mêmes problèmes. La différence est que modèle de ContentProvider vous guidera pour ne pas faire des erreurs de commom lors de l'accès base de données SQLite. ContentProvider docs dit: « Vous ne faites pas besoin d'un fournisseur d'utiliser une base de données SQLite si l'utilisation est tout à fait dans votre propre application. "

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

Montrons un exemple de la solution sémaphores présenté par Moss , que je pris CL. et improoved aux opérations de couverture.

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

Prendre en compte le fait que les bases de données SQLite fichier base et ne sont pas destinés à être en mesure d'accéder d'une manière multi-processus. La meilleure façon de procéder lors du mélange avec SQLite multi-traitement utilise des sémaphores (Aquire (), sortie ()) dans chaque base de données d'accès associées.

Si vous créez un Db wrapper que aquires / libère un sémaphore global de votre accès DB sera thread-safe. En effet cela signifie que vous pourriez obtenir un bootleneck parce que vous faire la queue l'accès à la DB. Donc, en plus, vous ne pouvez envelopper l'accès à l'aide sémaphores si elle est une opération qui modifie la base de données, alors que vous êtes ALTERIN le db ne sera en mesure d'accéder et d'attendre jusqu'à ce que le processus d'écriture est terminée.

Nous ne pouvions pas partager la connexion Db avec du fil multiple pour exécuter lu et opération d'écriture dans la base de données simultaniously.We devra faire objet unique de la DB en utilisant concept et nous allons syncronization exécuter une tâche à la fois .Nous utilisera singleton à faire l'objet DB et il sera dans les parts multiples threads.At un temps interprétera seule tâche. nous commencerons autre tâche ou toute opération sur DB. fournisseur de contenu n'est pas la solution de la question de verrouillage DB.

import java.util.concurrent.atomic.AtomicInteger;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class DatabaseManager {

private AtomicInteger mOpenCounter = new AtomicInteger();

private static DatabaseManager instance;
private static SQLiteOpenHelper mDatabaseHelper;
private SQLiteDatabase mDatabase;
//private static String DB_PATH = "";
//  private static String DB_NAME = "xyz.db";// Database name
private static String dbPathh;

public static synchronized void initializeInstance(SQLiteOpenHelper helper,
        String dbPath) {
    if (instance == null) {
        instance = new DatabaseManager();
        mDatabaseHelper = helper;
        dbPathh=dbPath;
    }
  }

public static synchronized DatabaseManager getInstance() {
    if (instance == null) {
        throw new IllegalStateException(DatabaseManager.class.getSimpleName() +
                " is not initialized, call initializeInstance(..) method first.");
    }

    return instance;
 }

  public synchronized SQLiteDatabase openDatabase(String thread) {

    if(mOpenCounter.get() == 0) {
        // Opening new database
        // mDatabase = mDatabaseHelper.getWritableDatabase();
        MyLog.e("Path Of DataBase", dbPathh);
        //  mDatabase=mDatabaseHelper.getWritableDatabase();
        mOpenCounter.incrementAndGet();
        mDatabase=SQLiteDatabase.openDatabase(dbPathh, null,   
 SQLiteDatabase.  CREATE_IF_NECESSARY|SQLiteDatabase.OPEN_READWRITE);   
        MyLog.e("Open Data Base", " New Connection created" +thread);
    }
    else{
        MyLog.e("Open Data Base", " Old Connection given " +thread);
    }
    //  Toast.makeText(NNacres.getConfig(), "open conn: present connection = 
   "   +mOpenCounter.get(), Toast.LENGTH_LONG).show();
    return mDatabase;
   }

    public synchronized void closeDatabase() {
    MyLog.e("Close db connection", ""+mOpenCounter.get());

    if(mOpenCounter.get() == 1) {
        // Closing database

        mDatabase.close();
        mOpenCounter.decrementAndGet();

        Log.e("DB CLOSED", "DONE");
    }
    //Toast.makeText(NNacres.getConfig(), "close conn: after close =   
 " +mOpenCounter.get(), Toast.LENGTH_LONG).show();
    }

    }

et d'écrire cette méthode dans votre classe d'aide YourSQLiteDataABse qui étend la classe SQLiteOpenHelper

     public SQLiteDatabase getWritableDatabase() {
DatabaseManager.initializeInstance(this,"data/data/your packgae name/databases/xyz");
    return DatabaseManager.getInstance().openDatabase(getClass().getSimpleName());

}



public static String getMyDbPath(String DB_NAME, Context context) {

    String myDbPath = context.getDatabasePath(DB_NAME).getPath();
    MyLog.e("DB Path: "+myDbPath);
    return myDbPath;
}

Vous devez être appeler getWritableDatabase() d'une fonction plutôt que le constructeur de la classe d'aide db. Si l'objet de classe d'aide db est créé avec SQLiteDatabase.openOrCreateDatabase(DB_PATH, null); ou similaire, puis getWritableDatabase() est appelé à partir d'une fonction, il va essayer de faire un appel synchrone DB provoquant une exception de verrouillage DB.

Vous parlez d'une seule action de l'utilisateur qui, à l'intérieur de votre programme, provoque plusieurs threads à exécuter, plus d'un qui peut être accéder à la base de données en mode de mise à jour?

C'est une mauvaise conception, période. Il n'y a aucun moyen pour vous de savoir dans quel ordre les discussions seront programmées par votre système d'exploitation (/ VM), et donc il n'y a aucun moyen pour vous de savoir dans quel ordre les accès base de données se produira, et qui est très probablement impliquer qu'il n'y a aucun moyen pour vous de savoir que la base de données des accès toujours se produire dans l'ordre que vous attendez.

Tous les accès base de données généré par / en provenance d'une certaine action de l'utilisateur doit être fait dans un seul fil.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top