13

我在 android sqlite 数据库和并发写入方面存在严重问题。为了更好地解释,我会给你一个现实生活中的例子:

我有一个桌面小部件,其中显示了我的数据库中的项目列表(在后台我有 DataService,它定期从我的远程服务器收集新数据,并用它们更新我的数据库)。所以 - 当我点击列表中的某个项目时,我需要更新数据库中点击的项目(=做写操作)。但是,当我准确地单击项目时,当 DataService 正在更新我的数据库中的新数据时,它当然会记录如下错误:

android.database.sqlite.SQLiteException: error code 5: database is locked

通常它很难模拟,但如果你安排 DataService 例如每 10 秒运行一次(只是为了演示),你可以很容易地模拟这个错误。

我的问题是,如何处理?我在文档中读到,如果同时有两个写事件,只有第一个会被执行,第二个会被记录为错误。听起来很奇怪,必须有其他选项,例如第二次写入会等到第一次写入完成。或者也许其他解决方案?试图阅读文档,但似乎谷歌文档中没有很好地涵盖该项目......几乎我拥有的所有信息,都是在官方页面以外的地方找到的。

PS:这是我的 DBHelper 类的缩短版本:

public class DBHelper extends SQLiteOpenHelper {

    private static final String TABLE_NEWS = "News";    
    private static final String COL_ID = "id";
    private static final String COL_TITLE = "title";
    private static final String COL_ALERT = "alert";

    public DBHelper(Context context) {
        super(context, "MY_DB_NAME", null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE " + TABLE_NEWS + "(" + COL_ID + " TEXT PRIMARY KEY," + COL_TITLE + " TEXT," + COL_ALERT + " INTEGER" + ")");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_NEWS);
        onCreate(db);
    }

    public void addRecords(ArrayList<NewsItem> items) {
        SQLiteDatabase db = this.getWritableDatabase();    
        for (int i = 0; i < items.size(); i++) {
            NewsItem item = items.get(i);    
            ContentValues values = new ContentValues();
            values.put(COL_ID, item.getId());
            values.put(COL_TITLE, item.getTitle());
            values.put(COL_ALERT, item.getAlertMe());    
            db.insert(TABLE_NEWS, null, values);
        }    
        db.close();
    }

    public int updateRecord(NewsItem item) {
        SQLiteDatabase db = this.getWritableDatabase();    
        ContentValues values = new ContentValues();
        values.put(COL_ALERT_ME, item.getAlertMe());
        int updated = db.update(TABLE_NEWS, values, COL_ID + " = ?", new String[] { item.getId() });
        db.close();    
        return updated;
    }
}
4

2 回答 2

23

SQLiteDatabase您需要在所有线程(及其托管组件)中使用单个对象来获得线程安全。让你DBHelper成为一个单身人士,或者使用ContentProvider, 来达到这个效果。

于 2012-05-29T12:19:12.560 回答
14

ContentProvider正是出于这个原因而创建的。您可以从多个线程调用它以进行插入/更新/删除操作。

http://developer.android.com/reference/android/content/ContentProvider.html

ContentProvider许多人认为,如果您想共享数据,您应该只想使用 a 。这是 a 的一大好处ContentProvider,但它不是唯一的好处。主要的好处是,在使用 时ContentProvider,Android 将为您管理数据库连接。

这是一个关于内容提供者的好教程

http://www.vogella.com/articles/AndroidSQLite/article.html

注意:虽然 ContentProvider 的 JavaDoc 确实声明它是共享数据所必需的,但这并不意味着它只能用于共享数据。在 Application Fundamentals 文档中,它也有关于 ContentProviders 的说法。

内容提供者对于读取和写入应用程序私有且不共享的数据也很有用。例如,Note Pad 示例应用程序使用内容提供程序来保存笔记。

http://developer.android.com/guide/topics/fundamentals.html#lcycles

于 2012-05-29T11:56:25.737 回答