#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. Таким образом, как активность, так и Служба могут использовать поставщика контента, и это решит проблемы с конфликтом в базе данных.