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

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

У меня есть виджет рабочего стола, на котором я показываю список элементов из моей базы данных (и в фоновом режиме у меня есть DataService, который через регулярные промежутки времени собирает свежие данные с моего удаленного сервера и обновляет мою базу данных с ними). Итак, когда я нажимаю на какой-либо элемент в списке, мне нужно обновить выбранный элемент (= сделать операцию записи) в базе данных. НО, когда я нажимаю на элемент точно в момент, когда 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;
    }
}
 qkx29 мая 2012 г., 13:57
Это правда, я делаю что-то подобное в моих службах: DBHelper dbHelper = new DBHelper (this); и т.д ... но как создать только один экземпляр - что произойдет, если мой процесс будет уничтожен? PS: пожалуйста, нажмите «Ответить на ваш запрос» - чем я могу написать непосредственно вам (если кто-то еще прокомментирует прямо здесь, это будет мульти чат)
 Richard16 окт. 2013 г., 16:30
Типичное добавление синхронизировано для каждого метода связано с открытием / закрытием базы данных.
 Simon Dorociak29 мая 2012 г., 13:32
Вы, вероятно, открываете и закрываете несколько соединений с базой данных в различных потоках. Это не очень хорошо. Вы должны открыть одно соединение с базой данных.

Ответы на вопрос(2)

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

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

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

Это хороший учебник по контент-провайдера

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

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

оставщики @Content также полезны для чтения и записи данных, которые являются частными для вашего приложения и не являются общими. Например, в примере приложения Note Pad для сохранения заметок используется поставщик контента.

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

 qkx29 мая 2012 г., 13:58
Может, я такой же, как и многие люди :), но когда я читаю документы,A content provider is only required if you need to share data between multiple applications - это не мой случай. Мне не нужно делить соединение БД между приложениями, только между сервисами. Но я действительно не очень разбираюсь в Android, так что это лучший способ справиться с такими проблемами?
 qkx29 мая 2012 г., 15:01
Хорошо, я пытался сделать мой DBHelper классом Singleton, и сейчас он работает отлично. В любом случае, спасибо, приятно знать, что можно использовать контент-провайдера для решения этой проблемы.
 stuckless29 мая 2012 г., 14:22
Снова, ТРЕБУЕТСЯ, если вы хотите обмениваться данными между приложениями, но это, безусловно, ЕДИНСТВЕННАЯ причина для его использования. Если вы читаете Javadoc дляinsert() (и другие методы), тогда вы увидите, что в нем четко указано, что он поддерживаетmultiple threads. Вы можете создать свой собственный поточный менеджер БД, или вы можете просто использовать контент-провайдера и позволить ему делать свою работу. Лично я не люблю использовать контент-провайдеров, но если бы я работал над несколькими потоками, я бы подумал об этом.
 stuckless29 мая 2012 г., 14:26
Но, в вашем случае, если вы не хотите использовать ContentProvider, то, возможно, сохраните доступный для записи экземпляр базы данных в вашем подклассе Application. Вы можете открыть его в onStart () и закрыть в onTerminate (). Ключевым моментом является обеспечение того, чтобы у вас никогда не было открыто более одного экземпляра базы данных с возможностью записи в любой момент времени, и жизненный цикл приложения является хорошим местом для этого.
 Alex Lockwood12 окт. 2012 г., 04:36
@ застрял я думаю, указав, чтоContentProvider поддерживает несколько потоков, так как причина его использования немного вводит в заблуждение ...SQLiteDatabase также может вызываться из нескольких потоков и часто используется в качестве источника данных для ContentProvider.
Решение Вопроса

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

 qkx29 мая 2012 г., 15:04
thanx, это именно то, что я хотел услышать - все возможные варианты :) Я решил сначала протестировать синглтон (это более просто), и он работает как шарм (пока), поэтому я закрываю вопрос.

Ваш ответ на вопрос