Одновременная запись в базу данных Android (из нескольких служб)?

У меня серьезная проблема с базой данных android sqlite и одновременным написанием. Для лучшего объяснения я приведу вам пример реальной жизни:

У меня есть виджет рабочего стола, в котором я показываю список элементов из моей базы данных (а на заднем плане у меня есть DataService, который через регулярные промежутки времени собирает свежие данные с моего удаленного сервера и обновляет мою базу данных). Итак - когда я нажимаю на какой-либо элемент в списке, мне нужно обновить элемент clicked (= do write operation) в базе данных. НО, когда я нажимаю на элемент точно в момент, когда DataService обновляет свежие данные в моей базе данных, он, конечно, регистрирует ошибку, подобную этой:

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

Обычно его трудно моделировать, но если вы планируете DataService запускать, например, каждые 10 секунд (только для демонстрации), вы можете легко имитировать эту ошибку.

И мой вопрос: как с этим справиться? Я прочитал в документах, что если в одно время будут два события записи, будет выполнено только первое, второе будет зарегистрировано как ошибка. Звучит странно, должны быть и другие варианты, например, вторая запись будет ждать до первого написания. Или, может быть, другое решение? Попытка читать документы, но кажется, что этот элемент не очень хорошо освещен в документах google... Почти у каждой информации у меня есть другие, кроме официальных страниц.

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

Ответ 1

Для обеспечения безопасности потоков вам необходимо использовать единственный объект SQLiteDatabase для всех потоков (и их компонентов хостинга). Сделайте свой DBHelper одним синглом или используйте ContentProvider, чтобы достичь этого эффекта.

Ответ 2

По этой причине был создан ContentProvider. Вы можете вызвать его из нескольких потоков для операций insert/update/delete.

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

Многие считают, что вам нужно использовать ContentProvider, если вы хотите обмениваться данными. Это большое преимущество ContentProvider, но это не единственное преимущество. Главное преимущество заключается в том, что при использовании ContentProvider Android будет управлять подключениями к базе данных для вас.

Это хорошее руководство для поставщиков контента

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

Примечание: Хотя JavaDoc для ContentProvider заявляет, что он НЕОБХОДИМО для обмена данными, это не означает, что он должен ТОЛЬКО использоваться для обмена данными. В документации по основам приложений это также говорит о ContentProviders.

Поставщики контента также полезны для чтения и записи данных, которые конфиденциально для вашего приложения и не используется. Например, блокнот Пример приложения использует поставщик контента для сохранения заметок.

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