并发写入android数据库(来自多个服务)?

时间:2012-05-29 11:28:06

标签: android sqlite android-service sqliteopenhelper

我对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;
    }
}

2 个答案:

答案 0 :(得分:22)

您需要在所有线程(及其托管组件)中使用单个SQLiteDatabase对象,以获得线程安全性。让您的DBHelper成为单身人士,或使用ContentProvider来实现此效果。

答案 1 :(得分:13)

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

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

如果您想共享数据,很多人都认为您只想使用ContentProvider。这是ContentProvider的一大好处,但并不是唯一的好处。主要的好处是,当使用ContentProvider时,Android将为您管理数据库连接。

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

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

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

  

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

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