Android SQLite DB, когда закрывать

79

Я работаю с базой данных SQLite на Android. Мой менеджер баз данных является одноэлементным и теперь открывает соединение с базой данных при ее инициализации. Можно ли оставить базу данных открытой все время, чтобы когда кто-то вызывал мой класс для работы с базой данных, он уже открыт? Или я должен открыть и закрыть базу данных до и после каждого доступа. Есть ли какой-либо вред, если просто оставить его открытым все время?

Спасибо!

Теги:

5 ответов

59
Лучший ответ

i будет держать его открытым все время и закрывать его в каком-то жизненном цикле, например onStop или onDestroy. Таким образом, вы можете легко проверить, используется ли база данных уже при вызове isDbLockedByCurrentThread или isDbLockedByOtherThreads на одном объекте SQLiteDatabase каждый раз, прежде чем использовать его. это предотвратит множественные манипуляции с базой данных и сохранит ваше приложение от потенциального сбоя.

поэтому в вашем singleton у вас может быть такой метод, чтобы получить ваш единственный объект SQLiteOpenHelper:

private SQLiteDatabase db;
private MyDBOpenHelper mySingletonHelperField;
public MyDBOpenHelper getDbHelper() {
    db = mySingletonHelperField.getDatabase();//returns the already created database object in my MyDBOpenHelper class(which extends `SQLiteOpenHelper`)
    while(db.isDbLockedByCurrentThread() || db.isDbLockedByOtherThreads()) {
        //db is locked, keep looping
    }
    return mySingletonHelperField;
}

поэтому всякий раз, когда вы хотите использовать свой открытый вспомогательный объект, вызовите этот метод getter (убедитесь, что он заштрихован)

другой метод в вашем singleton может быть (называемый КАЖДОЕ ВРЕМЯ, прежде чем пытаться вызвать геттер выше):

public void setDbHelper(MyDBOpenHelper mySingletonHelperField) {
    if(null == this.mySingletonHelperField) {
        this.mySingletonHelperField = mySingletonHelperField;
        this.mySingletonHelperField.setDb(this.mySingletonHelperField.getWritableDatabase());//creates and sets the database object in the MyDBOpenHelper class
    }
}

вы можете также закрыть базу данных в синглете:

public void finalize() throws Throwable {
    if(null != mySingletonHelperField)
        mySingletonHelperField.close();
    if(null != db)
        db.close();
    super.finalize();
}

если пользователи вашего приложения могут быстро создавать много взаимодействий с базами данных, вы должны использовать что-то вроде того, что я продемонстрировал выше. но если есть минимальные взаимодействия с базами данных, я бы не стал беспокоиться об этом и просто создавал и закрывал базу данных каждый раз.

  • 0
    При таком подходе я мог бы ускорить доступ к базе данных в 2 раза (оставьте базу данных открытой), спасибо
  • 2
    Спиннинг - очень плохая техника. Смотрите мой ответ ниже.
Показать ещё 8 комментариев
17

В настоящее время нет необходимости проверять, заблокирована ли база данных другим потоком. Хотя вы используете singleton SQLiteOpenHelper в каждом потоке, вы в безопасности. Из документации isDbLockedByCurrentThread:

Название этого метода происходит с момента, когда активен соединение с базой данных означало, что поток блокировка базы данных. В настоящее время больше нет истинной "базы данных" lock ", хотя потоки могут блокироваться, если они не могут получить базу данных соединение для выполнения конкретной операции.

isDbLockedByOtherThreads устарел с уровня API 16.

  • 0
    Нет смысла использовать один экземпляр на поток. SQLiteOpenHelper является потокобезопасным. Это также очень неэффективно для памяти. Вместо этого приложение должно хранить один экземпляр SQLiteOpenHelper для каждой базы данных. Для лучшего параллелизма рекомендуется использовать WriteAheadLogging, который обеспечивает пул соединений до 4-х соединений.
  • 0
    @ejboy Я имел в виду это. Msgstr "Использовать синглтон (= один) SQLiteOpenHelper в каждом потоке", а не "на поток".
8

Относительно вопросов:

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

Мы должны разделить "открытие DB", "открытие соединения". SQLiteOpenHelper.getWritableDatabase() предоставляет открытую БД. Но нам не нужно контролировать соединения, как это делается внутри.

Можно ли оставить базу данных открытой все время, чтобы когда кто-то звонил моему классу работать с базой данных, он уже открыт?

Да, это так. Соединения не зависают, если транзакции должным образом закрыты. Обратите внимание, что ваша БД также будет автоматически закрыта, если GC завершает ее.

Или мне нужно открыть и закрыть базу данных до и после каждого доступа.

Закрытие экземпляра SQLiteDatabase не дает ничего потрясающего, кроме закрытия соединений, но это плохой разработчик, если в настоящий момент есть некоторые соединения. Кроме того, после SQLiteDatabase.close() SQLiteOpenHelper.getWritableDatabase() вернет новый экземпляр.

Есть ли какой-либо вред, если просто оставить его открытым все время?

Нет, нет. Также обратите внимание, что закрытие БД в несвязанный момент и поток, например. в Activity.onStop() может закрыть активные соединения и оставить данные в несогласованном состоянии.

  • 0
    Спасибо, прочитав ваши слова "после SQLiteDatabase.close (), SQLiteOpenHelper.getWritableDatabase () вернет новый экземпляр", я понял, что наконец-то у меня есть ответ на старую проблему моего приложения. К проблеме, которая стала критической после перехода на Android 9 (см. Stackoverflow.com/a/54224922/297710 )
0

Вы также можете использовать ContentProvider. Он сделает это для вас.

-7

Создайте свой собственный контекст приложения, затем откройте и закройте базу данных. Этот объект также имеет метод OnTerminate(), который можно использовать для закрытия соединения. Я еще не пробовал, но, похоже, это лучший подход.

@binnyb: Мне не нравится использовать finalize(), чтобы закрыть соединение. Могу работать, но из того, что я понимаю, писать код в Java finalize() - это плохая идея.

  • 0
    Вы не хотите полагаться на завершение, потому что никогда не знаете, когда оно будет вызвано. Объект может оставаться в подвешенном состоянии до тех пор, пока GC не решит его очистить, и только после этого будет вызываться finalize (). Вот почему это бесполезно. Правильный способ сделать это состоит в том, чтобы иметь метод, который вызывается, когда объект больше не используется (независимо от того, является ли он сборщиком мусора или нет), и это именно то, чем является onTerminate ().
  • 5
    В документах ясно onTerminate что onTerminate не будет вызываться в производственной среде - developer.android.com/reference/android/app/…

Ещё вопросы

Сообщество Overcoder
Наверх
Меню