30

AsyncTasksデータベースのテーブルとカーソルにアクセスするために使用しています。

残念ながら、データベースがロックされているという例外が時折見られます。

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)

読み取りスレッドとは異なるスレッドからデータベースに書き込むコードの一般的な例と、スレッドの安全性を確保するにはどうすればよいですか。

私が持っていた 1 つの提案はContentProvider、複数のスレッドからのデータベースへのアクセスを処理するため、 を使用することです。これを見ていきますが、これはそのような問題を処理するための推奨される方法ですか? 前か後ろかということを考えるとかなり重量級に見えます。

4

7 に答える 7

28

最後にa を使用しContentProviderました。これは問題を解決するように見えました。

于 2010-04-23T09:42:55.957 に答える
16

すべてのデータベースのオープンにクローズがあることを確認し、(さらに重要なことに) これを保証するために、各データベース インスタンスのスコープをそれを必要とするメソッドに対してのみローカルにすることで、この同じ例外を解決しました。ContentProvider は、複数のスレッドから db にアクセスするときに使用するのに適した安全なクラスですが、適切な db プラクティスを使用していることも確認してください。

  • db インスタンスをローカルに保つ (SQLiteDatabase クラス メンバーなし!)
  • close()それが開かれたのと同じメソッドでデータベースを呼び出す
  • close()データベースから取得したカーソルを呼び出す
  • SQLiteDatabse が持つ可能性のある苦情については、LogCat に耳を傾けてください。
于 2012-02-28T16:29:59.837 に答える
9

SQLite データベースはファイル ベースであり、マルチプロセスでアクセスできるようには設計されていないことを考慮してください。SQLite とマルチプロセッシングを組み合わせる最良の手順は、各データベース関連のアクセスでセマフォ (aquire()、release()) を使用することです。

グローバルセマフォを取得/解放する Db ラッパーを作成すると、DB アクセスはスレッドセーフになります。実際、これは、DB へのアクセスをキューに入れているため、ブートルネックが発生する可能性があることを意味します。さらに、データベースを変更する操作である場合にのみ、セマフォを使用してアクセスをラップできます。そのため、データベースを変更している間は、誰もアクセスできず、書き込みプロセスが完了するまで待つことができません。

于 2010-04-17T11:36:54.643 に答える
1

データベースで読み取りおよび書き込み操作を同時に実行するために、複数のスレッドで Db 接続を共有できませんでした。同期化の概念を使用して DB の単一オブジェクトを作成する必要があり、一度に 1 つのタスクを実行します。DB を作成するためにシングルトン パターンを使用します。オブジェクトであり、複数のスレッド内で共有されます。一度に単一のタスクを実行します。次に、 DB で他のタスクまたは操作を開始します。コンテンツ プロバイダーは、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();
    }

    }

SQLiteOpenHelper クラスを拡張する YourSQLiteDataABse ヘルパー クラスにこのメソッドを記述します。

     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;
}
于 2014-07-11T12:23:07.050 に答える
-3

getWritableDatabase()db ヘルパー クラスのコンストラクターではなく、関数から呼び出す必要があります。db ヘルパー クラス オブジェクトが で作成された SQLiteDatabase.openOrCreateDatabase(DB_PATH, null);getWritableDatabase()、関数から呼び出されると、DB への同期呼び出しを試み、DB ロック例外が発生します。

于 2011-11-29T16:56:42.177 に答える
-6

プログラム内で複数のスレッドが実行され、そのうちの複数が更新モードでデータベースにアクセスしている可能性がある単一のユーザーアクションについて話しているのですか?

それは悪いデザインです、ピリオド。OS (/VM) によってスレッドがスケジュールされる順序を知る方法がないため、データベース アクセスがどの順序で発生するかを知る方法はありません。データベースへのアクセスが常に期待どおりの順序で行われるかどうかを知る方法はありません。

何らかのユーザー アクションによって生成された、またはユーザー アクションから発生したすべてのデータベース アクセスは、すべて 1 つの単一スレッドで実行する必要があります。

于 2010-04-15T20:29:20.213 に答える