Доступ к базе данных SQLite из нескольких потоков

#android #sqlite

#Android #sqlite

Вопрос:

Рассмотрим следующее: у меня есть Service , который записывает в базу данных в AsyncTask . И моя активность считывает данные из базы данных (для простоты рассмотрим поток пользовательского интерфейса). Я получаю доступ к базе данных с помощью SQLiteOpenHelper . Я создаю один экземпляр в приложении onCreate (), а затем получаю его в сервисе и activity. Есть ли какая-либо вероятность того, что я получу «мертвую блокировку» своей базы данных? Ранее я использовал ContentProvider для таких операций. Хотя она основана на использовании одного SQLiteOpenHelper экземпляра, я решил упростить свой проект, исключив ContentProvider .

Рассмотрим код:

 public class App extends Application {

    private OpenHelper openHelper;

    @Override
    public void onCreate(){
        super.onCreate();
            openHelper=new OpenHelper();
    }

        public OpenHelper getHelper(){
            return openHelper;
        }
}
  

В процессе работы:

 OpenHelper helper=(App)getApplication().getHelper();
SQLiteDatabase db=helper.getReadableDatabase();
// Do reading
  

И внутри Serice, в отдельном потоке:

 OpenHelper helper=(App)getApplication().getHelper();
SQLiteDatabase db=helper.getWritableDatabase();
//Do writing
  

Было бы безопасно?

Это UPD может быть решением, но не уверен, как его использовать.

Ответ №1:

Поздний, запоздалый ответ. Вы в полном порядке. На самом деле, это правильный способ сделать это. Смотрите мой пост в блоге: http://touchlabblog.tumblr.com/post/24474750219/single-sqlite-connection /. Просмотрите мой профиль здесь. Множество примеров этого. ContentProvdier — это просто большие накладные расходы, и он не нужен, если вы не предоставляете доступ к данным за пределами своего приложения. Транзакции хороши для ускорения работы и (очевидно) улучшения согласованности, но не нужны.

Просто используйте один SQLiteOpenHelper в своем приложении, и вы в безопасности.

Комментарии:

1. @seb отредактировал ответ. Перенесенные блоги несколько месяцев назад. Также посмотрите на сообщение о блокировке sqlite: touchlabblog.tumblr.com/post/24474398246/android-sqlite-locking . Ключевым моментом здесь является то, что Android выполняет блокировку потоков под капотом для вас в Java-среде, но ТОЛЬКО в одном экземпляре. Несколько потоков будут мешать друг другу. Кроме того, в сообщении говорится, что чтение базы данных выполняется в потоке пользовательского интерфейса. Пожалуйста, обратите внимание: не делайте этого. Когда-либо.

2. @KevinGalligan Я просмотрел исходные тексты для SQLiteDatabase и Helper и убежден, что вы ошибаетесь. объект базы данных создает объект SQLiteStatement, который, в свою очередь, использует SQLiteSession, каждый поток будет иметь свой собственный SQLiteSession. В SQLiteSession javadoc конкретно указано, что класс не является потокобезопасным. Любопытно, что заставляет вас так говорить, потому что я действительно хочу, чтобы вы были правы…

3. @seb Думай, что тебе нравится. Вы ошибаетесь. Извините. За несколько лет у меня было много приложений с миллионами пользователей. Это работает.

4. @seb ОК. Будучи менее $ $ дырявым. ВСПОМОГАТЕЛЬНЫМ элементом является синглтон. НЕ к БАЗЕ ДАННЫХ. ПОМОЩНИК хранит один экземпляр базы данных. Смотрите здесь: grepcode.com/file/repository.grepcode.com/java/ext /…

5. @seb Если у вас есть один помощник, все в порядке. Я не делаю никаких заявлений в противном случае.

Ответ №2:

Держу пари: это небезопасно.

Чтобы быть в более безопасном положении, вы должны использовать транзакции SQL. Начните с beginTransaction() или beginTransactionNonExclusive() и закончите endTransaction() . Как показано здесь

Комментарии:

1. Begintransactiononexclusive () выдает мне ошибку, связанную с указанным выше api11. но минимальный API для моих приложений равен 10. есть ли другой способ использовать sqlite в немедленном режиме?

Ответ №3:

Это мое решение, я создал класс и частный статический объект для синхронизации всего доступа к базе данных

 public class DBFunctions {
// ...
private static Object lockdb = new Object();


/**
 * Do something using DB
 */
public boolean doInsertRecord(final RecordBean beanRecord) {
    // ...
    boolean success = false;

    synchronized (lockdb) {
              // ...
              // 
              // here ... the access to db is in exclusive way
              // 

              // ...
      final SQLiteStatement statement = db.compileStatement(sqlQuery);

        try {
            // execute ...
            statement.execute(); 
            statement.close();

            // ok
            success = true;
        } catch (Exception e) {
            // error
            success = false;
        }
            }

       return success;
    }
  

}

Я попытался использовать АСИНХРОННУЮ задачу, и она работает нормально. Я надеюсь, что это правильный способ решить проблему.

Есть другие предложения???

Комментарии:

1. Одновременный запуск трех асинхронных задач, при этом все они вставляют много-много строк в одну таблицу. (Все записи в одну таблицу.) Похоже, что задачи находятся в очереди и, следовательно, ошибка не выдается. Это решение работает для меня. Тем не менее, мне интересно, безопасно ли это решение для блокировки. Есть мнения?

Ответ №4:

Хороший вопрос. Моя первая мысль — это было бы небезопасно. Однако, согласно документам SQLite, SQLite можно использовать в 3 режимах. Режим по умолчанию — «сериализованный» режим:

Сериализованный. В сериализованном режиме SQLite может безопасно использоваться несколькими потоками без каких-либо ограничений

Итак, я предполагаю, что это скомпилировано в сериализованном режиме на Android.

Ответ №5:

Просто увидел это, когда искал что-то еще. Похоже, что эта проблема была бы эффективно решена с помощью ContentProvider. Таким образом, как активность, так и Служба могут использовать поставщика контента, и это решит проблемы с конфликтом в базе данных.