5

我正在使用 SQLiteOpenHelper 在 Android 上写入和读取 SQlite 数据库。当用户单击 UI 时,我使用 AsyncTask 从 SQLite 数据库中读取数据,但在同一时刻,我正在使用其他 AsyncTask 在后台更新和写入数据库。

每 x 次我得到数据库锁定异常。我怎样才能解决这个问题?可以同时从多个线程以某种方式访问​​ SQlite 吗?

我是这样使用它的:我有一个从 SQLiteOpenHelper 扩展的数据库类。我实现了 onCreate 和 onUpgrade 方法,每次我从数据库读取或写入数据库时​​,我都使用 SQLiteDatabase:

SQLiteDatabase database = null;
try {

    database = new Database(context).getWritableDatabase();

    ....
    writing and reading from database...
    ....

} catch (Exception e) {
    e.printStackTrace();
} finally {
    if (database != null) {
        database.close();
    }
}

最后,如果我使用 SQLiteStatements 和 Cursors,我也会关闭它们。我应该使用@Justin 答案并在应用程序中有一个单一的数据库类吗?

这是我得到的错误:

E/SqliteDatabaseCpp(17377): sqlite3_open_v2("/data/data/com.skulptur/databases/LGM.s3db", &handle, 6, NULL) failed
E/SQLiteDatabase(17377): Failed to open the database. closing it.
E/SQLiteDatabase(17377):     android.database.sqlite.SQLiteDatabaseLockedException: database is locked
E/SQLiteDatabase(17377):    at android.database.sqlite.SQLiteDatabase.dbopen(Native Method)
E/SQLiteDatabase(17377):    at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:983)
E/SQLiteDatabase(17377):    at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:956)
E/SQLiteDatabase(17377):    at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:1021)
E/SQLiteDatabase(17377):    at   android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:750)
E/SQLiteDatabase(17377):    at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:221)
E/SQLiteDatabase(17377):    at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:149)
E/SQLiteDatabase(17377):    at android.database.sqlite.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:223)
4

4 回答 4

7

这是我为避免锁定问题所做的。我让我的应用程序处理我的数据库助手的单个实例,并始终引用它来处理我的数据库。

public class MyApp extends Application {
    // My instance of SQLiteOpenHelper
    public static DbHelper openHelper;

    @Override
    public void onCreate() {
        super.onCreate();
        if (openHelper == null) {
            openHelper = new DbHelper(this);
        }
    }

    public synchronized static SQLiteDatabase getDB() {
        return openHelper.getWritableDatabase();
    }
}

一旦你这样做了,任何时候你想要做一个数据库查询,得到像 MyApp.getDB() 这样的数据库句柄,你就永远不会遇到锁定问题。确保永远不要关闭数据库。这种模式在您的应用程序中始终保持单一连接。SQLite 意味着在 Android 上是同步的,所以如果你有长时间运行的 DB 进程,比如在单个事务中插入 1000 次,你需要这样编码:

db.beginTransaction();
try {
    for (DBRow row : insertList) {
        // your insert code
        insertRow(row);
        db.yieldIfContendedSafely();
    }
    db.setTransactionSuccessful();
} finally {
    db.endTransaction();
}

这将允许其他查询自行插入,这样您就不会看到您的应用程序在后台更新期间停止运行。

于 2012-10-19T15:28:10.500 回答
3

使用Loaders在数据库之上实现一个ContentProvider ,它将处理线程。可以参考本教程:http ://www.vogella.com/articles/AndroidSQLite/article.html

于 2012-10-19T13:43:51.237 回答
1

有几件事可以尝试。首先尝试将相同的实例传递SQLiteOpenHelper给您的每个任务。其次是在 Helper 内的 SQLiteDatabase 上使用 beginTransaction 和 endTransaction。第三是添加synchronized到任务中使用的每个辅助方法。

于 2012-10-19T12:49:39.663 回答
0

我认为您错误地使用了数据库连接。它可能是由未关闭的游标或其他原因引起的。Sqlite 数据库实现是线程安全的,可以从不同的线程同时访问。请提供有关您的问题的更多详细信息(异常堆栈跟踪、导致异常的代码示例等),以便我们为您提供帮助。

于 2012-10-19T12:50:39.883 回答